// Copyright (C) 2016 The Qt Company Ltd. // Copyright (C) 2016 Intel Corporation. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QDEBUG_H #define QDEBUG_H #if 0 #pragma qt_class(QtDebug) #endif #include #include #include #include #include #include #include #include #include // all these have already been included by various headers above, but don't rely on indirect includes: #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if !defined(QT_LEAN_HEADERS) || QT_LEAN_HEADERS < 1 # include # include # include # include #endif QT_BEGIN_NAMESPACE class QT6_ONLY(Q_CORE_EXPORT) QDebug : public QIODeviceBase { friend class QMessageLogger; friend class QDebugStateSaver; friend class QDebugStateSaverPrivate; struct Stream { enum { VerbosityShift = 29, VerbosityMask = 0x7 }; explicit Stream(QIODevice *device) : ts(device) {} explicit Stream(QString *string) : ts(string, WriteOnly) {} explicit Stream(QByteArray *ba) : ts(ba, WriteOnly) {} explicit Stream(QtMsgType t) : ts(&buffer, WriteOnly), type(t), message_output(true) {} QTextStream ts; QString buffer; int ref = 1; QtMsgType type = QtDebugMsg; bool space = true; bool noQuotes = false; bool message_output = false; int verbosity = DefaultVerbosity; QMessageLogContext context; } *stream; enum Latin1Content { ContainsBinary = 0, ContainsLatin1 }; QT7_ONLY(Q_CORE_EXPORT) void putUcs4(uint ucs4); QT7_ONLY(Q_CORE_EXPORT) void putString(const QChar *begin, size_t length); QT7_ONLY(Q_CORE_EXPORT) void putByteArray(const char *begin, size_t length, Latin1Content content); QT7_ONLY(Q_CORE_EXPORT) void putTimeUnit(qint64 num, qint64 den); QT7_ONLY(Q_CORE_EXPORT) void putInt128(const void *i); QT7_ONLY(Q_CORE_EXPORT) void putUInt128(const void *i); QT7_ONLY(Q_CORE_EXPORT) void putQtOrdering(QtOrderingPrivate::QtOrderingTypeFlag flags, Qt::partial_ordering order); template using if_streamable = std::enable_if_t< std::conjunction_v...> , bool>; public: explicit QDebug(QIODevice *device) : stream(new Stream(device)) {} explicit QDebug(QString *string) : stream(new Stream(string)) {} explicit QDebug(QByteArray *bytes) : stream(new Stream(bytes)) {} explicit QDebug(QtMsgType t) : stream(new Stream(t)) {} QDebug(const QDebug &o) : stream(o.stream) { ++stream->ref; } QDebug(QDebug &&other) noexcept : stream{std::exchange(other.stream, nullptr)} {} inline QDebug &operator=(const QDebug &other); QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QDebug) ~QDebug(); void swap(QDebug &other) noexcept { qt_ptr_swap(stream, other.stream); } QT7_ONLY(Q_CORE_EXPORT) QDebug &resetFormat(); inline QDebug &space() { stream->space = true; stream->ts << ' '; return *this; } inline QDebug &nospace() { stream->space = false; return *this; } inline QDebug &maybeSpace() { if (stream->space) stream->ts << ' '; return *this; } inline QDebug &verbosity(int verbosityLevel) { stream->verbosity = verbosityLevel; return *this; } int verbosity() const { return stream->verbosity; } void setVerbosity(int verbosityLevel) { stream->verbosity = verbosityLevel; } enum VerbosityLevel { MinimumVerbosity = 0, DefaultVerbosity = 2, MaximumVerbosity = 7 }; bool autoInsertSpaces() const { return stream->space; } void setAutoInsertSpaces(bool b) { stream->space = b; } [[nodiscard]] bool quoteStrings() const noexcept { return !stream->noQuotes; } void setQuoteStrings(bool b) { stream->noQuotes = !b; } inline QDebug "e() { stream->noQuotes = false; return *this; } inline QDebug &noquote() { stream->noQuotes = true; return *this; } inline QDebug &maybeQuote(char c = '"') { if (!stream->noQuotes) stream->ts << c; return *this; } inline QDebug &operator<<(QChar t) { putUcs4(t.unicode()); return maybeSpace(); } inline QDebug &operator<<(bool t) { stream->ts << (t ? "true" : "false"); return maybeSpace(); } inline QDebug &operator<<(char t) { stream->ts << t; return maybeSpace(); } inline QDebug &operator<<(signed short t) { stream->ts << t; return maybeSpace(); } inline QDebug &operator<<(unsigned short t) { stream->ts << t; return maybeSpace(); } inline QDebug &operator<<(char16_t t) { return *this << QChar(t); } inline QDebug &operator<<(char32_t t) { putUcs4(t); return maybeSpace(); } inline QDebug &operator<<(signed int t) { stream->ts << t; return maybeSpace(); } inline QDebug &operator<<(unsigned int t) { stream->ts << t; return maybeSpace(); } inline QDebug &operator<<(signed long t) { stream->ts << t; return maybeSpace(); } inline QDebug &operator<<(unsigned long t) { stream->ts << t; return maybeSpace(); } inline QDebug &operator<<(qint64 t) { stream->ts << t; return maybeSpace(); } inline QDebug &operator<<(quint64 t) { stream->ts << t; return maybeSpace(); } inline QDebug &operator<<(qfloat16 t) { stream->ts << t; return maybeSpace(); } inline QDebug &operator<<(float t) { stream->ts << t; return maybeSpace(); } inline QDebug &operator<<(double t) { stream->ts << t; return maybeSpace(); } inline QDebug &operator<<(const char* t) { stream->ts << QString::fromUtf8(t); return maybeSpace(); } inline QDebug &operator<<(const char16_t *t) { stream->ts << QStringView(t); return maybeSpace(); } inline QDebug &operator<<(const QString & t) { putString(t.constData(), size_t(t.size())); return maybeSpace(); } inline QDebug &operator<<(QStringView s) { putString(s.data(), size_t(s.size())); return maybeSpace(); } inline QDebug &operator<<(QUtf8StringView s) { putByteArray(reinterpret_cast(s.data()), s.size(), ContainsBinary); return maybeSpace(); } inline QDebug &operator<<(QLatin1StringView t) { putByteArray(t.latin1(), t.size(), ContainsLatin1); return maybeSpace(); } inline QDebug &operator<<(const QByteArray & t) { putByteArray(t.constData(), t.size(), ContainsBinary); return maybeSpace(); } inline QDebug &operator<<(QByteArrayView t) { putByteArray(t.constData(), t.size(), ContainsBinary); return maybeSpace(); } inline QDebug &operator<<(const void * t) { stream->ts << t; return maybeSpace(); } inline QDebug &operator<<(std::nullptr_t) { stream->ts << "(nullptr)"; return maybeSpace(); } inline QDebug &operator<<(std::nullopt_t) { stream->ts << "nullopt"; return maybeSpace(); } inline QDebug &operator<<(QTextStreamFunction f) { stream->ts << f; return *this; } inline QDebug &operator<<(QTextStreamManipulator m) { stream->ts << m; return *this; } #ifdef Q_QDOC template QDebug &operator<<(const std::basic_string &s); template QDebug &operator<<(std::basic_string_view s); #else template QDebug &operator<<(const std::basic_string &s) { return *this << QUtf8StringView(s); } template QDebug &operator<<(std::basic_string_view s) { return *this << QUtf8StringView(s); } #ifdef __cpp_char8_t template QDebug &operator<<(const std::basic_string &s) { return *this << QUtf8StringView(s); } template QDebug &operator<<(std::basic_string_view s) { return *this << QUtf8StringView(s); } #endif // __cpp_char8_t template QDebug &operator<<(const std::basic_string &s) { return *this << QStringView(s); } template QDebug &operator<<(std::basic_string_view s) { return *this << QStringView(s); } template QDebug &operator<<(const std::basic_string &s) { if constexpr (sizeof(wchar_t) == 2) return *this << QStringView(s); else return *this << QString::fromWCharArray(s.data(), s.size()); // ### optimize } template QDebug &operator<<(std::basic_string_view s) { if constexpr (sizeof(wchar_t) == 2) return *this << QStringView(s); else return *this << QString::fromWCharArray(s.data(), s.size()); // ### optimize } template QDebug &operator<<(const std::basic_string &s) { return *this << QString::fromUcs4(s.data(), s.size()); } template QDebug &operator<<(std::basic_string_view s) { return *this << QString::fromUcs4(s.data(), s.size()); } #endif // !Q_QDOC template QDebug &operator<<(std::chrono::duration duration) { stream->ts << duration.count(); putTimeUnit(Period::num, Period::den); return maybeSpace(); } #ifdef QT_SUPPORTS_INT128 private: // Constrained templates so they only match q(u)int128 without conversions. // Also keeps these operators out of the ABI. template using if_qint128 = std::enable_if_t, bool>; template using if_quint128 = std::enable_if_t, bool>; public: template = true> QDebug &operator<<(T i128) { putInt128(&i128); return maybeSpace(); } template = true> QDebug &operator<<(T u128) { putUInt128(&u128); return maybeSpace(); } #endif // QT_SUPPORTS_INT128 private: template static void streamTypeErased(QDebug &d, const void *obj) { d << *static_cast(obj); } using StreamTypeErased = void(*)(QDebug&, const void*); QT7_ONLY(Q_CORE_EXPORT) static QString toStringImpl(StreamTypeErased s, const void *obj); QT7_ONLY(Q_CORE_EXPORT) static QByteArray toBytesImpl(StreamTypeErased s, const void *obj); QT7_ONLY(Q_CORE_EXPORT) QDebug &putTupleLikeImplImpl(const char *ns, const char *what, size_t n, StreamTypeErased *ops, const void **data); template QDebug &putTupleLikeImpl(const char *ns, const char *what, const TupleLike &t, std::index_sequence) { if constexpr (sizeof...(Is)) { StreamTypeErased ops[] = { &streamTypeErased>>... }; const void *data[] = { std::addressof(std::get(t))... }; return putTupleLikeImplImpl(ns, what, sizeof...(Is), ops, data); } else { return putTupleLikeImplImpl(ns, what, 0, nullptr, nullptr); } } template QDebug &putTupleLike(const char *ns, const char *what, const TupleLike &t) { using Indexes = std::make_index_sequence>; return putTupleLikeImpl(ns, what, t, Indexes{}); } public: template static QString toString(const T &object) { return toStringImpl(&streamTypeErased, std::addressof(object)); } template static QByteArray toBytes(const T &object) { return toBytesImpl(&streamTypeErased, std::addressof(object)); } template = true> QDebug &operator<<(const std::tuple &t) { return putTupleLike("std", "tuple", t); } template = true> QDebug &operator<<(const std::optional &o) { if (!o) return *this << std::nullopt; StreamTypeErased s = &streamTypeErased>; const void *d = std::addressof(*o); return putTupleLikeImplImpl("std", "optional", 1, &s, &d); } private: template using if_ordering_type = std::enable_if_t, bool>; template = true> friend QDebug operator<<(QDebug debug, T t) { debug.putQtOrdering(QtOrderingPrivate::orderingFlagsFor(t), Qt::partial_ordering(t)); return debug; } }; Q_DECLARE_SHARED(QDebug) class QDebugStateSaverPrivate; class QDebugStateSaver { public: Q_NODISCARD_CTOR Q_CORE_EXPORT QDebugStateSaver(QDebug &dbg); Q_CORE_EXPORT ~QDebugStateSaver(); private: Q_DISABLE_COPY(QDebugStateSaver) std::unique_ptr d; }; class QNoDebug { public: inline QNoDebug &operator<<(QTextStreamFunction) { return *this; } inline QNoDebug &operator<<(QTextStreamManipulator) { return *this; } inline QNoDebug &space() { return *this; } inline QNoDebug &nospace() { return *this; } inline QNoDebug &maybeSpace() { return *this; } inline QNoDebug "e() { return *this; } inline QNoDebug &noquote() { return *this; } inline QNoDebug &maybeQuote(const char = '"') { return *this; } inline QNoDebug &verbosity(int) { return *this; } template inline QNoDebug &operator<<(const T &) { return *this; } }; inline QNoDebug QMessageLogger::noDebug(...) const noexcept { return {}; } inline QDebug &QDebug::operator=(const QDebug &other) { QDebug{other}.swap(*this); return *this; } namespace QtPrivate { template inline QDebug printSequentialContainer(QDebug debug, const char *which, const SequentialContainer &c) { const QDebugStateSaver saver(debug); debug.nospace() << which << '('; typename SequentialContainer::const_iterator it = c.begin(), end = c.end(); if (it != end) { debug << *it; ++it; } while (it != end) { debug << ", " << *it; ++it; } debug << ')'; return debug; } template inline QDebug printAssociativeContainer(QDebug debug, const char *which, const AssociativeContainer &c) { const QDebugStateSaver saver(debug); debug.nospace() << which << "("; for (typename AssociativeContainer::const_iterator it = c.constBegin(); it != c.constEnd(); ++it) { debug << '(' << it.key() << ", " << it.value() << ')'; } debug << ')'; return debug; } } // namespace QtPrivate template using QDebugIfHasDebugStream = std::enable_if_t...>, QDebug>; template using QDebugIfHasDebugStreamContainer = std::enable_if_t...>, QDebug>; #ifndef Q_QDOC template inline QDebugIfHasDebugStreamContainer, T> operator<<(QDebug debug, const QList &vec) { return QtPrivate::printSequentialContainer(std::move(debug), "QList", vec); } template inline QDebugIfHasDebugStream operator<<(QDebug debug, const QVarLengthArray &vec) { return QtPrivate::printSequentialContainer(std::move(debug), "QVarLengthArray", vec); } template inline QDebugIfHasDebugStream operator<<(QDebug debug, const std::vector &vec) { return QtPrivate::printSequentialContainer(std::move(debug), "std::vector", vec); } template inline QDebugIfHasDebugStream operator<<(QDebug debug, const std::array &array) { return QtPrivate::printSequentialContainer(std::move(debug), "std::array", array); } template inline QDebugIfHasDebugStream operator<<(QDebug debug, const std::list &vec) { return QtPrivate::printSequentialContainer(std::move(debug), "std::list", vec); } template inline QDebugIfHasDebugStream operator<<(QDebug debug, std::initializer_list list) { return QtPrivate::printSequentialContainer(std::move(debug), "std::initializer_list", list); } template inline QDebugIfHasDebugStream operator<<(QDebug debug, const std::map &map) { return QtPrivate::printSequentialContainer(std::move(debug), "std::map", map); // yes, sequential: *it is std::pair } template inline QDebugIfHasDebugStream operator<<(QDebug debug, const std::multimap &map) { return QtPrivate::printSequentialContainer(std::move(debug), "std::multimap", map); // yes, sequential: *it is std::pair } template inline QDebugIfHasDebugStream operator<<(QDebug debug, const std::multiset &multiset) { return QtPrivate::printSequentialContainer(std::move(debug), "std::multiset", multiset); } template inline QDebugIfHasDebugStream operator<<(QDebug debug, const std::set &set) { return QtPrivate::printSequentialContainer(std::move(debug), "std::set", set); } template inline QDebugIfHasDebugStream operator<<(QDebug debug, const std::unordered_map &unordered_map) { return QtPrivate::printSequentialContainer(std::move(debug), "std::unordered_map", unordered_map); // yes, sequential: *it is std::pair } template inline QDebugIfHasDebugStream operator<<(QDebug debug, const std::unordered_set &unordered_set) { return QtPrivate::printSequentialContainer(std::move(debug), "std::unordered_set", unordered_set); } template inline QDebugIfHasDebugStreamContainer, Key, T> operator<<(QDebug debug, const QMap &map) { return QtPrivate::printAssociativeContainer(std::move(debug), "QMap", map); } template inline QDebugIfHasDebugStreamContainer, Key, T> operator<<(QDebug debug, const QMultiMap &map) { return QtPrivate::printAssociativeContainer(std::move(debug), "QMultiMap", map); } template inline QDebugIfHasDebugStreamContainer, Key, T> operator<<(QDebug debug, const QHash &hash) { return QtPrivate::printAssociativeContainer(std::move(debug), "QHash", hash); } template inline QDebugIfHasDebugStreamContainer, Key, T> operator<<(QDebug debug, const QMultiHash &hash) { return QtPrivate::printAssociativeContainer(std::move(debug), "QMultiHash", hash); } template inline QDebugIfHasDebugStream operator<<(QDebug debug, const std::pair &pair) { const QDebugStateSaver saver(debug); debug.nospace() << "std::pair(" << pair.first << ", " << pair.second << ')'; return debug; } template inline QDebugIfHasDebugStreamContainer, T> operator<<(QDebug debug, const QSet &set) { return QtPrivate::printSequentialContainer(std::move(debug), "QSet", set); } template inline QDebugIfHasDebugStream operator<<(QDebug debug, const QContiguousCache &cache) { const QDebugStateSaver saver(debug); debug.nospace() << "QContiguousCache("; for (qsizetype i = cache.firstIndex(); i <= cache.lastIndex(); ++i) { debug << cache[i]; if (i != cache.lastIndex()) debug << ", "; } debug << ')'; return debug; } #else template QDebug operator<<(QDebug debug, const QList &list); template QDebug operator<<(QDebug debug, const QVarLengthArray &array); template QDebug operator<<(QDebug debug, const std::vector &vec); template QDebug operator<<(QDebug debug, const std::array &array); template QDebug operator<<(QDebug debug, const std::list &vec); template QDebug operator<<(QDebug debug, const std::map &map); template QDebug operator<<(QDebug debug, const std::multimap &map); template QDebug operator<<(QDebug debug, const QMap &map); template QDebug operator<<(QDebug debug, const QMultiMap &map); template QDebug operator<<(QDebug debug, const QHash &hash); template QDebug operator<<(QDebug debug, const QMultiHash &hash); template QDebug operator<<(QDebug debug, const QSet &set); template QDebug operator<<(QDebug debug, const std::pair &pair); template QDebug operator<<(QDebug debug, const QContiguousCache &cache); template QDebug operator<<(QDebug debug, const std::multiset &multiset); template QDebug operator<<(QDebug debug, const std::set &set); template QDebug operator<<(QDebug debug, const std::unordered_map &unordered_map); template QDebug operator<<(QDebug debug, const std::unordered_set &unordered_set); #endif // Q_QDOC template inline QDebug operator<<(QDebug debug, const QSharedPointer &ptr) { QDebugStateSaver saver(debug); debug.nospace() << "QSharedPointer(" << ptr.data() << ")"; return debug; } template class QTaggedPointer; template inline QDebug operator<<(QDebug debug, const QTaggedPointer &ptr) { QDebugStateSaver saver(debug); debug.nospace() << "QTaggedPointer(" << ptr.pointer() << ", " << ptr.tag() << ")"; return debug; } Q_CORE_EXPORT QDebug qt_QMetaEnum_debugOperator(QDebug&, qint64 value, const QMetaObject *meta, const char *name); Q_CORE_EXPORT QDebug qt_QMetaEnum_flagDebugOperator(QDebug &dbg, quint64 value, const QMetaObject *meta, const char *name); Q_CORE_EXPORT void qt_QMetaEnum_flagDebugOperator(QDebug &debug, size_t sizeofT, uint value); Q_CORE_EXPORT void qt_QMetaEnum_flagDebugOperator(QDebug &debug, size_t sizeofT, quint64 value); template void qt_QMetaEnum_flagDebugOperator(QDebug &debug, size_t sizeofT, Int value) { static_assert(std::is_unsigned_v, "Cast value to an unsigned type before calling this function"); const QDebugStateSaver saver(debug); debug.resetFormat(); debug.nospace() << "QFlags(" << Qt::hex << Qt::showbase; bool needSeparator = false; for (size_t i = 0; i < sizeofT * 8; ++i) { if (value & (Int(1) << i)) { if (needSeparator) debug << '|'; else needSeparator = true; debug << (Int(1) << i); } } debug << ')'; } template ::value, bool> = true> inline QDebug operator<<(QDebug debug, Flags flags) { using T = typename Flags::enum_type; using UInt = typename QIntegerForSizeof::Unsigned; #if !defined(QT_NO_QOBJECT) if constexpr (QtPrivate::IsQEnumHelper::Value || QtPrivate::IsQEnumHelper::Value) { // if QFlags is a Q_FLAG, we always zero-extend; if not, we need to // allow it to sign-extend if it is signed (see QMetaEnum::valueToKeys) using Int = std::conditional_t::Value, UInt, std::underlying_type_t>; const QMetaObject *obj = qt_getEnumMetaObject(T()); const char *name = qt_getEnumName(T()); return qt_QMetaEnum_flagDebugOperator(debug, Int(flags.toInt()), obj, name); } else #endif { qt_QMetaEnum_flagDebugOperator(debug, sizeof(T), UInt(flags.toInt())); return debug; } } #ifdef Q_QDOC template QDebug operator<<(QDebug debug, const QFlags &flags); #endif // Q_QDOC #if !defined(QT_NO_QOBJECT) && !defined(Q_QDOC) // Debugging of plain enums. There are three cases: // 1) the enum is part of a Q_DECLARE_FLAGS and there's a Q_FLAG for that // -> debugs as that QFlags (even if a Q_ENUM is present) // 2) the enum is declared a Q_ENUM but is not part of a Q_DECLARE_FLAGS // -> debugs via qt_QMetaEnum_debugOperator() // 3) the enum is not associated with a QMetaObject // -> no streaming // To avoid ambiguity in overload resolution, the template conditions are // mutually exclusive. namespace QtPrivate { template , bool = sizeof(T) <= sizeof(quint64)> struct EnumHasQFlag { static constexpr bool Value = false; }; template struct EnumHasQFlag : QtPrivate::IsQEnumHelper> {}; template , bool HasQFlag = EnumHasQFlag::Value> struct EnumHasQEnum { static constexpr bool Value = false; }; template struct EnumHasQEnum : QtPrivate::IsQEnumHelper {}; } template std::enable_if_t::Value, QDebug> // case 1 operator<<(QDebug debug, T flag) { return debug << QFlags(flag); } template std::enable_if_t::Value, QDebug> // case 2 operator<<(QDebug dbg, T value) { const QMetaObject *obj = qt_getEnumMetaObject(value); const char *name = qt_getEnumName(value); return qt_QMetaEnum_debugOperator(dbg, static_cast::type>(value), obj, name); } #endif // !QT_NO_QOBJECT && !Q_QDOC inline QDebug operator<<(QDebug debug, QKeyCombination combination) { QDebugStateSaver saver(debug); debug.nospace() << "QKeyCombination(" << combination.keyboardModifiers() << ", " << combination.key() << ")"; return debug; } #ifdef Q_OS_DARWIN // We provide QDebug stream operators for commonly used Core Foundation // and Core Graphics types, as well as NSObject. Additional CF/CG types // may be added by the user, using Q_DECLARE_QDEBUG_OPERATOR_FOR_CF_TYPE. #define QT_FOR_EACH_CORE_FOUNDATION_TYPE(F) \ F(CFArray) \ F(CFURL) \ F(CFData) \ F(CFNumber) \ F(CFDictionary) \ F(CFLocale) \ F(CFDate) \ F(CFBoolean) \ F(CFTimeZone) \ #define QT_FOR_EACH_MUTABLE_CORE_FOUNDATION_TYPE(F) \ F(CFError) \ F(CFBundle) \ #define QT_FOR_EACH_CORE_GRAPHICS_TYPE(F) \ F(CGPath) \ #define QT_FOR_EACH_MUTABLE_CORE_GRAPHICS_TYPE(F) \ F(CGColorSpace) \ F(CGImage) \ F(CGFont) \ F(CGColor) \ #define QT_FORWARD_DECLARE_CF_TYPE(type) Q_FORWARD_DECLARE_CF_TYPE(type); #define QT_FORWARD_DECLARE_MUTABLE_CF_TYPE(type) Q_FORWARD_DECLARE_MUTABLE_CF_TYPE(type); #define QT_FORWARD_DECLARE_CG_TYPE(type) Q_FORWARD_DECLARE_CG_TYPE(type); #define QT_FORWARD_DECLARE_MUTABLE_CG_TYPE(type) Q_FORWARD_DECLARE_MUTABLE_CG_TYPE(type); QT_END_NAMESPACE Q_FORWARD_DECLARE_CF_TYPE(CFString); struct objc_object; Q_FORWARD_DECLARE_OBJC_CLASS(NSObject); QT_FOR_EACH_CORE_FOUNDATION_TYPE(QT_FORWARD_DECLARE_CF_TYPE) QT_FOR_EACH_MUTABLE_CORE_FOUNDATION_TYPE(QT_FORWARD_DECLARE_MUTABLE_CF_TYPE) QT_FOR_EACH_CORE_GRAPHICS_TYPE(QT_FORWARD_DECLARE_CG_TYPE) QT_FOR_EACH_MUTABLE_CORE_GRAPHICS_TYPE(QT_FORWARD_DECLARE_MUTABLE_CG_TYPE) QT_BEGIN_NAMESPACE #define QT_FORWARD_DECLARE_QDEBUG_OPERATOR_FOR_CF_TYPE(CFType) \ Q_CORE_EXPORT QDebug operator<<(QDebug, CFType##Ref); #define Q_DECLARE_QDEBUG_OPERATOR_FOR_CF_TYPE(CFType) \ QDebug operator<<(QDebug debug, CFType##Ref ref) \ { \ if (!ref) \ return debug << QT_STRINGIFY(CFType) "Ref(0x0)"; \ if (CFStringRef description = CFCopyDescription(ref)) { \ QDebugStateSaver saver(debug); \ debug.noquote() << description; \ CFRelease(description); \ } \ return debug; \ } // Defined in qcore_mac_objc.mm #if defined(__OBJC__) Q_CORE_EXPORT QDebug operator<<(QDebug, id); #endif Q_CORE_EXPORT QDebug operator<<(QDebug, objc_object *); Q_CORE_EXPORT QDebug operator<<(QDebug, const NSObject *); Q_CORE_EXPORT QDebug operator<<(QDebug, CFStringRef); QT_FOR_EACH_CORE_FOUNDATION_TYPE(QT_FORWARD_DECLARE_QDEBUG_OPERATOR_FOR_CF_TYPE) QT_FOR_EACH_MUTABLE_CORE_FOUNDATION_TYPE(QT_FORWARD_DECLARE_QDEBUG_OPERATOR_FOR_CF_TYPE) QT_FOR_EACH_CORE_GRAPHICS_TYPE(QT_FORWARD_DECLARE_QDEBUG_OPERATOR_FOR_CF_TYPE) QT_FOR_EACH_MUTABLE_CORE_GRAPHICS_TYPE(QT_FORWARD_DECLARE_QDEBUG_OPERATOR_FOR_CF_TYPE) #undef QT_FORWARD_DECLARE_CF_TYPE #undef QT_FORWARD_DECLARE_MUTABLE_CF_TYPE #undef QT_FORWARD_DECLARE_CG_TYPE #undef QT_FORWARD_DECLARE_MUTABLE_CG_TYPE #endif // Q_OS_DARWIN QT_END_NAMESPACE #endif // QDEBUG_H