Skip to content

Commit

Permalink
QFlags: simplify operator<< for QDebug
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
thiagomacieira committed Aug 14, 2024
1 parent 05aaf2a commit 0c707c6
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 54 deletions.
94 changes: 46 additions & 48 deletions src/corelib/io/qdebug.h
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,8 @@ inline QDebug operator<<(QDebug debug, const QTaggedPointer<T, Tag> &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 <typename Int>
Expand All @@ -546,66 +548,62 @@ void qt_QMetaEnum_flagDebugOperator(QDebug &debug, size_t sizeofT, Int value)
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);

template<typename T>
typename std::enable_if<QtPrivate::IsQEnumHelper<T>::Value, QDebug>::type
operator<<(QDebug dbg, T value)
template <class Flags,
typename = std::enable_if_t<QtPrivate::IsQFlags<Flags>::value, void>,
typename T = typename Flags::enum_type>
inline QDebug operator<<(QDebug debug, Flags flags)
{
const QMetaObject *obj = qt_getEnumMetaObject(value);
const char *name = qt_getEnumName(value);
return qt_QMetaEnum_debugOperator(dbg, static_cast<typename std::underlying_type<T>::type>(value), obj, name);
using UInt = typename QIntegerForSizeof<T>::Unsigned;
#if !defined(QT_NO_QOBJECT)
if constexpr (QtPrivate::IsQEnumHelper<T>::Value || QtPrivate::IsQEnumHelper<Flags>::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;
}
}

template<typename T,
typename A = typename std::enable_if<std::is_enum<T>::value, void>::type,
typename B = typename std::enable_if<sizeof(T) <= sizeof(int), void>::type,
typename C = typename std::enable_if<!QtPrivate::IsQEnumHelper<T>::Value, void>::type,
typename D = typename std::enable_if<QtPrivate::IsQEnumHelper<QFlags<T>>::Value, void>::type>
inline QDebug operator<<(QDebug dbg, T value)
{
typedef QFlags<T> FlagsT;
const QMetaObject *obj = qt_getEnumMetaObject(FlagsT());
const char *name = qt_getEnumName(FlagsT());
return qt_QMetaEnum_debugOperator(dbg, typename FlagsT::Int(value), obj, name);
}
#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.

template <class T>
inline typename std::enable_if<
QtPrivate::IsQEnumHelper<T>::Value || QtPrivate::IsQEnumHelper<QFlags<T> >::Value,
QDebug>::type
qt_QMetaEnum_flagDebugOperator_helper(QDebug debug, const QFlags<T> &flags)
{
using UInt = typename QIntegerForSizeof<T>::Unsigned;
const QMetaObject *obj = qt_getEnumMetaObject(T());
const char *name = qt_getEnumName(T());
return qt_QMetaEnum_flagDebugOperator(debug, UInt(flags.toInt()), obj, name);
namespace QtPrivate {
template <typename T, bool IsEnum = std::is_enum_v<T>, bool SizedForQFlags = sizeof(T) <= 4>
struct EnumHasQFlag { static constexpr bool Value = false; };
template <typename T> struct EnumHasQFlag<T, true, true> : QtPrivate::IsQEnumHelper<QFlags<T>> {};

template <typename T, bool IsEnum = std::is_enum_v<T>, bool HasQFlag = EnumHasQFlag<T>::Value>
struct EnumHasQEnum { static constexpr bool Value = false; };
template <typename T> struct EnumHasQEnum<T, true, false> : QtPrivate::IsQEnumHelper<T> {};
}

template <class T>
inline typename std::enable_if<
!QtPrivate::IsQEnumHelper<T>::Value && !QtPrivate::IsQEnumHelper<QFlags<T> >::Value,
QDebug>::type
qt_QMetaEnum_flagDebugOperator_helper(QDebug debug, const QFlags<T> &flags)
#else // !QT_NO_QOBJECT && !Q_QDOC
template <class T>
inline QDebug qt_QMetaEnum_flagDebugOperator_helper(QDebug debug, const QFlags<T> &flags)
#endif
template <typename T>
std::enable_if_t<QtPrivate::EnumHasQFlag<T>::Value, QDebug> // case 1
operator<<(QDebug debug, T flag)
{
using UInt = typename QIntegerForSizeof<T>::Unsigned;
qt_QMetaEnum_flagDebugOperator(debug, sizeof(T), UInt(flags.toInt()));
return debug;
return debug << QFlags(flag);
}

template<typename T>
inline QDebug operator<<(QDebug debug, const QFlags<T> &flags)
std::enable_if_t<QtPrivate::EnumHasQEnum<T>::Value, QDebug> // case 2
operator<<(QDebug dbg, T value)
{
// We have to use an indirection otherwise specialisation of some other overload of the
// operator<< the compiler would try to instantiate QFlags<T> for the std::enable_if
return qt_QMetaEnum_flagDebugOperator_helper(debug, flags);
const QMetaObject *obj = qt_getEnumMetaObject(value);
const char *name = qt_getEnumName(value);
return qt_QMetaEnum_debugOperator(dbg, static_cast<typename std::underlying_type<T>::type>(value), obj, name);
}
#endif // !QT_NO_QOBJECT && !Q_QDOC

inline QDebug operator<<(QDebug debug, QKeyCombination combination)
{
Expand Down
1 change: 1 addition & 0 deletions tests/auto/corelib/io/qdebug/tst_qdebug.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1508,4 +1508,5 @@ TestClassA& operator<< (TestClassA& s, T&) { return s; };
template<> TestClassA& operator<< <TestClassB>(TestClassA& s, TestClassB& l);

QTEST_MAIN(tst_QDebug);

#include "tst_qdebug.moc"
14 changes: 8 additions & 6 deletions tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<Qt::WindowType>(WindowTitleHint) QFlags<Qt::WindowType>(Window) "
"QFlags<Qt::WindowType>(Desktop) QFlags<Qt::WindowType>(WindowSystemMenuHint)"
<< "hello QFlags<MyNamespace::MyClass::MyFlag>(MyFlag1) world"
<< "QFlags<MyNamespace::MyClass::MyFlag>(MyFlag1) QFlags<MyNamespace::MyClass::MyFlag>(MyFlag2|MyFlag3)"
<< "QFlags<MyNamespace::MyClass::MyScopedFlag>(MyFlag2)"
<< "QFlags<MyNamespace::MyClass::MyScopedFlag>(MyFlag2|MyFlag3)"
<< "MyNamespace::MyClass::MyFlag1";
<< "QFlags<MyNamespace::MyClass::MyFlag>(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<Qt::WindowType>(WindowTitleHint) QFlags<Qt::WindowType>(Window) "
"QFlags<Qt::WindowType>(Desktop) QFlags<Qt::WindowType>(WindowSystemMenuHint)"
<< "hello QFlags<MyNamespace::MyClass::MyFlag>(MyFlag1) world"
<< "QFlags<MyNamespace::MyClass::MyFlag>(MyFlag1) QFlags<MyNamespace::MyClass::MyFlag>(MyFlag2|MyFlag3)"
<< "QFlags<MyNamespace::MyClass::MyScopedFlag>(MyFlag2)"
<< "QFlags<MyNamespace::MyClass::MyScopedFlag>(MyFlag2|MyFlag3)"
<< "MyNamespace::MyClass::MyFlag::MyFlag1";
<< "QFlags<MyNamespace::MyClass::MyFlag>(MyFlag1)";
}

void tst_QMetaObject::enumDebugStream()
Expand Down

0 comments on commit 0c707c6

Please sign in to comment.