From 0c707c6ebece18677deaccccdfec5d48c587106f Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Fri, 2 Aug 2024 10:14:19 -0700 Subject: [PATCH] QFlags: simplify operator<< for QDebug Instead of declaring three different template overloads (with an #if in the middle), reduce to explicitly two: 1) one for the Q_ENUMs that aren't QFlags 2) one for QFlags This does change how an enum that is a Q_FLAG gets printed: previously, it was printed as itself, now it's printed as QFlags, even if no QFlags are present. Then use if constexpr for that second one to get the enumeration from QMetaObject, if the flags are associated with a QMetaObject. Change-Id: Ifb754f0e28774c20aa7cfffd17e7f8b9c68b292b Reviewed-by: Fabian Kosmale --- src/corelib/io/qdebug.h | 100 +++++++++--------- tests/auto/corelib/io/qdebug/tst_qdebug.cpp | 1 + .../kernel/qmetaobject/tst_qmetaobject.cpp | 14 +-- 3 files changed, 58 insertions(+), 57 deletions(-) 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()