QDebug: improve support for printing 64-bit QFlags

Like the 32-bit version, we add an explicitly-exported non-template
overload. And we can call the 64-bit implementation from the 32-bit one
to save in code generation inside QtCore.

Task-number: QTBUG-111926
Change-Id: I8a96935cf6c742259c9dfffd17e9928218333c04
Reviewed-by: Ahmad Samir <a.samirh78@gmail.com>
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
Thiago Macieira 2024-08-07 15:23:39 -07:00
parent 2971dbfe06
commit 094f7cab54
3 changed files with 67 additions and 6 deletions

View File

@ -1329,9 +1329,19 @@ QDebugStateSaver::~QDebugStateSaver()
*/ */
void qt_QMetaEnum_flagDebugOperator(QDebug &debug, size_t sizeofT, uint value) void qt_QMetaEnum_flagDebugOperator(QDebug &debug, size_t sizeofT, uint value)
{ {
qt_QMetaEnum_flagDebugOperator<uint>(debug, sizeofT, value); qt_QMetaEnum_flagDebugOperator(debug, sizeofT, quint64(value));
} }
/*!
\internal
Ditto, for 64-bit.
*/
void qt_QMetaEnum_flagDebugOperator(QDebug &debug, size_t sizeofT, quint64 value)
{
qt_QMetaEnum_flagDebugOperator<quint64>(debug, sizeofT, value);
}
#ifndef QT_NO_QOBJECT #ifndef QT_NO_QOBJECT
/*! /*!
\internal \internal
@ -1449,7 +1459,7 @@ QDebug qt_QMetaEnum_flagDebugOperator(QDebug &debug, quint64 value, const QMetaO
debug << '('; debug << '(';
} }
debug << me.valueToKeys(static_cast<int>(value)); debug << me.valueToKeys(value);
if (enumScope) if (enumScope)
debug << ')'; debug << ')';

View File

@ -526,6 +526,7 @@ inline QDebug operator<<(QDebug debug, const QTaggedPointer<T, Tag> &ptr)
Q_CORE_EXPORT QDebug qt_QMetaEnum_debugOperator(QDebug&, qint64 value, const QMetaObject *meta, const char *name); 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 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, uint value);
Q_CORE_EXPORT void qt_QMetaEnum_flagDebugOperator(QDebug &debug, size_t sizeofT, quint64 value);
template <typename Int> template <typename Int>
void qt_QMetaEnum_flagDebugOperator(QDebug &debug, size_t sizeofT, Int value) void qt_QMetaEnum_flagDebugOperator(QDebug &debug, size_t sizeofT, Int value)
@ -556,9 +557,13 @@ inline QDebug operator<<(QDebug debug, Flags flags)
using UInt = typename QIntegerForSizeof<T>::Unsigned; using UInt = typename QIntegerForSizeof<T>::Unsigned;
#if !defined(QT_NO_QOBJECT) #if !defined(QT_NO_QOBJECT)
if constexpr (QtPrivate::IsQEnumHelper<T>::Value || QtPrivate::IsQEnumHelper<Flags>::Value) { if constexpr (QtPrivate::IsQEnumHelper<T>::Value || QtPrivate::IsQEnumHelper<Flags>::Value) {
// if QFlags<T> 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<QtPrivate::IsQEnumHelper<Flags>::Value, UInt,
std::underlying_type_t<T>>;
const QMetaObject *obj = qt_getEnumMetaObject(T()); const QMetaObject *obj = qt_getEnumMetaObject(T());
const char *name = qt_getEnumName(T()); const char *name = qt_getEnumName(T());
return qt_QMetaEnum_flagDebugOperator(debug, UInt(flags.toInt()), obj, name); return qt_QMetaEnum_flagDebugOperator(debug, Int(flags.toInt()), obj, name);
} else } else
#endif #endif
{ {

View File

@ -54,9 +54,14 @@ class tst_QDebug: public QObject
public: public:
enum EnumType { EnumValue1 = 1, EnumValue2 = INT_MIN }; enum EnumType { EnumValue1 = 1, EnumValue2 = INT_MIN };
enum FlagType { EnumFlag1 = 1, EnumFlag2 = INT_MIN }; enum FlagType { EnumFlag1 = 1, EnumFlag2 = INT_MIN };
enum Enum64Type { Enum64Value1 = 1, Enum64Value2 = Q_UINT64_C(0x1'0000'0002) };
enum Flag64Type : qlonglong { Enum64Flag1 = 1, Enum64Flag2 = Q_UINT64_C(0x1'0000'0002) };
Q_ENUM(EnumType) Q_ENUM(EnumType)
Q_ENUM(Enum64Type)
Q_DECLARE_FLAGS(Flags, FlagType) Q_DECLARE_FLAGS(Flags, FlagType)
Q_FLAG(Flags) Q_FLAG(Flags)
Q_DECLARE_FLAGS(Flags64, Flag64Type)
Q_FLAG(Flags64)
private slots: private slots:
void assignment() const; void assignment() const;
@ -88,6 +93,7 @@ private slots:
void qDebugQByteArray() const; void qDebugQByteArray() const;
void qDebugQByteArrayView() const; void qDebugQByteArrayView() const;
void qDebugQFlags() const; void qDebugQFlags() const;
void qDebugQFlags64() const;
void qDebugStdChrono_data() const; void qDebugStdChrono_data() const;
void qDebugStdChrono() const; void qDebugStdChrono() const;
void qDebugStdOptional() const; void qDebugStdOptional() const;
@ -106,6 +112,8 @@ private slots:
void objcInObjcMode() const; void objcInObjcMode() const;
#endif #endif
}; };
Q_DECLARE_OPERATORS_FOR_FLAGS(tst_QDebug::Flags)
Q_DECLARE_OPERATORS_FOR_FLAGS(tst_QDebug::Flags64)
void tst_QDebug::assignment() const void tst_QDebug::assignment() const
{ {
@ -1096,8 +1104,9 @@ void tst_QDebug::qDebugQFlags() const
QString file, function; QString file, function;
int line = 0; int line = 0;
QFlags<TestEnum> flags(Flag1 | Flag2 | SignFlag); QFlags<TestEnum> flags(Flag1 | Flag2 | SignFlag);
MessageHandlerSetter mhs(myMessageHandler); MessageHandlerSetter mhs(myMessageHandler);
// first, test QFlags on an enum where neither are Q_FLAG or Q_ENUM
{ qDebug() << flags; } { qDebug() << flags; }
#ifndef QT_NO_MESSAGELOGCONTEXT #ifndef QT_NO_MESSAGELOGCONTEXT
file = __FILE__; line = __LINE__ - 2; function = Q_FUNC_INFO; file = __FILE__; line = __LINE__ - 2; function = Q_FUNC_INFO;
@ -1108,17 +1117,54 @@ void tst_QDebug::qDebugQFlags() const
QCOMPARE(s_line, line); QCOMPARE(s_line, line);
QCOMPARE(QString::fromLatin1(s_function), function); QCOMPARE(QString::fromLatin1(s_function), function);
// Test the output of QFlags with an enum not declared with Q_DECLARE_FLAGS and Q_FLAGS // QFlags where the backing enum is a Q_ENUM but the QFlags isn't a QFLAG
QFlags<EnumType> flags2(EnumValue2); QFlags<EnumType> flags2(EnumValue2);
qDebug() << flags2; qDebug() << flags2;
QCOMPARE(s_msg, QString::fromLatin1("QFlags<tst_QDebug::EnumType>(EnumValue2)")); QCOMPARE(s_msg, QString::fromLatin1("QFlags<tst_QDebug::EnumType>(EnumValue2)"));
// A now for one that was fully declared // And now for one that was fully declared
tst_QDebug::Flags flags3(EnumFlag1); tst_QDebug::Flags flags3(EnumFlag1);
qDebug() << flags3; qDebug() << flags3;
QCOMPARE(s_msg, QString::fromLatin1("QFlags<tst_QDebug::FlagType>(EnumFlag1)")); QCOMPARE(s_msg, QString::fromLatin1("QFlags<tst_QDebug::FlagType>(EnumFlag1)"));
} }
enum class TestEnum64 : qulonglong {
Flag1 = 0x1,
Flag2 = Q_UINT64_C(0x8000'0000'0000'0000)
};
Q_DECLARE_FLAGS(TestFlags64, TestEnum64)
Q_DECLARE_OPERATORS_FOR_FLAGS(TestFlags64)
void tst_QDebug::qDebugQFlags64() const
{
QString file, function;
int line = 0;
QFlags<TestEnum64> flags(TestEnum64::Flag1 | TestEnum64::Flag2);
MessageHandlerSetter mhs(myMessageHandler);
// first, test QFlags on an enum where neither are Q_FLAG or Q_ENUM
qDebug() << flags;
#ifndef QT_NO_MESSAGELOGCONTEXT
file = __FILE__; line = __LINE__ - 2; function = Q_FUNC_INFO;
#endif
QCOMPARE(s_msgType, QtDebugMsg);
QCOMPARE(s_msg, "QFlags(0x1|0x8000000000000000)");
QCOMPARE(QString::fromLatin1(s_file), file);
QCOMPARE(s_line, line);
QCOMPARE(QString::fromLatin1(s_function), function);
// QFlags where the backing enum is a Q_ENUM but the QFlags isn't a QFLAG
QFlags<Enum64Type> flags2(Enum64Value2);
qDebug() << flags2;
QCOMPARE(s_msg, QString::fromLatin1("QFlags<tst_QDebug::Enum64Type>(Enum64Value2)"));
// And now for one that was fully declared
tst_QDebug::Flags64 flags3(Enum64Flag1|Enum64Flag2);
qDebug() << flags3;
QCOMPARE(s_msg, QString::fromLatin1("QFlags<tst_QDebug::Flag64Type>(Enum64Flag1|Enum64Flag2)"));
}
using ToStringFunction = std::function<QString()>; using ToStringFunction = std::function<QString()>;
void tst_QDebug::qDebugStdChrono_data() const void tst_QDebug::qDebugStdChrono_data() const
{ {