diff --git a/src/corelib/io/qdebug.h b/src/corelib/io/qdebug.h index 901e7d9476c..36cc09edb2e 100644 --- a/src/corelib/io/qdebug.h +++ b/src/corelib/io/qdebug.h @@ -523,6 +523,8 @@ inline QDebug operator<<(QDebug debug, const QTaggedPointer &ptr) 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); template @@ -546,66 +548,62 @@ void qt_QMetaEnum_flagDebugOperator(QDebug &debug, size_t sizeofT, Int value) debug << ')'; } +template ::value, void>, + typename T = typename Flags::enum_type> +inline QDebug operator<<(QDebug debug, Flags flags) +{ + using UInt = typename QIntegerForSizeof::Unsigned; +#if !defined(QT_NO_QOBJECT) + if constexpr (QtPrivate::IsQEnumHelper::Value || QtPrivate::IsQEnumHelper::Value) { + const QMetaObject *obj = qt_getEnumMetaObject(T()); + const char *name = qt_getEnumName(T()); + return qt_QMetaEnum_flagDebugOperator(debug, UInt(flags.toInt()), obj, name); + } else +#endif + { + qt_QMetaEnum_flagDebugOperator(debug, sizeof(T), UInt(flags.toInt())); + return debug; + } +} + #if !defined(QT_NO_QOBJECT) && !defined(Q_QDOC) -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); +// 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 SizedForQFlags = sizeof(T) <= 4> +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 -typename std::enable_if::Value, QDebug>::type +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); } - -template::value, void>::type, - typename B = typename std::enable_if::type, - typename C = typename std::enable_if::Value, void>::type, - typename D = typename std::enable_if>::Value, void>::type> -inline QDebug operator<<(QDebug dbg, T value) -{ - typedef QFlags FlagsT; - const QMetaObject *obj = qt_getEnumMetaObject(FlagsT()); - const char *name = qt_getEnumName(FlagsT()); - return qt_QMetaEnum_debugOperator(dbg, typename FlagsT::Int(value), obj, name); -} - -template -inline typename std::enable_if< - QtPrivate::IsQEnumHelper::Value || QtPrivate::IsQEnumHelper >::Value, - QDebug>::type -qt_QMetaEnum_flagDebugOperator_helper(QDebug debug, const QFlags &flags) -{ - using UInt = typename QIntegerForSizeof::Unsigned; - const QMetaObject *obj = qt_getEnumMetaObject(T()); - const char *name = qt_getEnumName(T()); - return qt_QMetaEnum_flagDebugOperator(debug, UInt(flags.toInt()), obj, name); -} - -template -inline typename std::enable_if< - !QtPrivate::IsQEnumHelper::Value && !QtPrivate::IsQEnumHelper >::Value, - QDebug>::type -qt_QMetaEnum_flagDebugOperator_helper(QDebug debug, const QFlags &flags) -#else // !QT_NO_QOBJECT && !Q_QDOC -template -inline QDebug qt_QMetaEnum_flagDebugOperator_helper(QDebug debug, const QFlags &flags) -#endif -{ - using UInt = typename QIntegerForSizeof::Unsigned; - qt_QMetaEnum_flagDebugOperator(debug, sizeof(T), UInt(flags.toInt())); - return debug; -} - -template -inline QDebug operator<<(QDebug debug, const QFlags &flags) -{ - // We have to use an indirection otherwise specialisation of some other overload of the - // operator<< the compiler would try to instantiate QFlags for the std::enable_if - return qt_QMetaEnum_flagDebugOperator_helper(debug, flags); -} +#endif // !QT_NO_QOBJECT && !Q_QDOC inline QDebug operator<<(QDebug debug, QKeyCombination combination) { diff --git a/tests/auto/corelib/io/qdebug/tst_qdebug.cpp b/tests/auto/corelib/io/qdebug/tst_qdebug.cpp index d9c5c7a84de..14d503076c2 100644 --- a/tests/auto/corelib/io/qdebug/tst_qdebug.cpp +++ b/tests/auto/corelib/io/qdebug/tst_qdebug.cpp @@ -1508,4 +1508,5 @@ TestClassA& operator<< (TestClassA& s, T&) { return s; }; template<> TestClassA& operator<< (TestClassA& s, TestClassB& l); QTEST_MAIN(tst_QDebug); + #include "tst_qdebug.moc" diff --git a/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp b/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp index 182ec6daaef..2220e194fd5 100644 --- a/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp +++ b/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp @@ -2883,32 +2883,34 @@ void tst_QMetaObject::enumDebugStream_data() QTest::newRow("verbosity=1") << 1 << "hello MyEnum::MyEnum2 world" << "hello MyScopedEnum::Enum3 scoped world" - << "WindowType::WindowTitleHint WindowType::Window WindowType::Desktop WindowType::WindowSystemMenuHint" + << "WindowType(WindowTitleHint) WindowType(Window) WindowType(Desktop) WindowType(WindowSystemMenuHint)" << "hello MyFlag(MyFlag1) world" << "MyFlag(MyFlag1) MyFlag(MyFlag2|MyFlag3)" << "MyScopedFlag(MyFlag2)" << "MyScopedFlag(MyFlag2|MyFlag3)" - << "MyFlag::MyFlag1"; + << "MyFlag(MyFlag1)"; QTest::newRow("verbosity=2") << 2 << "hello MyNamespace::MyClass::MyEnum2 world" << "hello MyNamespace::MyClass::MyScopedEnum::Enum3 scoped world" - << "Qt::WindowTitleHint Qt::Window Qt::Desktop Qt::WindowSystemMenuHint" + << "QFlags(WindowTitleHint) QFlags(Window) " + "QFlags(Desktop) QFlags(WindowSystemMenuHint)" << "hello QFlags(MyFlag1) world" << "QFlags(MyFlag1) QFlags(MyFlag2|MyFlag3)" << "QFlags(MyFlag2)" << "QFlags(MyFlag2|MyFlag3)" - << "MyNamespace::MyClass::MyFlag1"; + << "QFlags(MyFlag1)"; QTest::newRow("verbosity=3") << 3 << "hello MyNamespace::MyClass::MyEnum::MyEnum2 world" << "hello MyNamespace::MyClass::MyScopedEnum::Enum3 scoped world" - << "Qt::WindowType::WindowTitleHint Qt::WindowType::Window Qt::WindowType::Desktop Qt::WindowType::WindowSystemMenuHint" + << "QFlags(WindowTitleHint) QFlags(Window) " + "QFlags(Desktop) QFlags(WindowSystemMenuHint)" << "hello QFlags(MyFlag1) world" << "QFlags(MyFlag1) QFlags(MyFlag2|MyFlag3)" << "QFlags(MyFlag2)" << "QFlags(MyFlag2|MyFlag3)" - << "MyNamespace::MyClass::MyFlag::MyFlag1"; + << "QFlags(MyFlag1)"; } void tst_QMetaObject::enumDebugStream()