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)
{
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
/*!
\internal
@ -1449,7 +1459,7 @@ QDebug qt_QMetaEnum_flagDebugOperator(QDebug &debug, quint64 value, const QMetaO
debug << '(';
}
debug << me.valueToKeys(static_cast<int>(value));
debug << me.valueToKeys(value);
if (enumScope)
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_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 <typename Int>
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;
#if !defined(QT_NO_QOBJECT)
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 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
#endif
{

View File

@ -54,9 +54,14 @@ class tst_QDebug: public QObject
public:
enum EnumType { EnumValue1 = 1, EnumValue2 = 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(Enum64Type)
Q_DECLARE_FLAGS(Flags, FlagType)
Q_FLAG(Flags)
Q_DECLARE_FLAGS(Flags64, Flag64Type)
Q_FLAG(Flags64)
private slots:
void assignment() const;
@ -88,6 +93,7 @@ private slots:
void qDebugQByteArray() const;
void qDebugQByteArrayView() const;
void qDebugQFlags() const;
void qDebugQFlags64() const;
void qDebugStdChrono_data() const;
void qDebugStdChrono() const;
void qDebugStdOptional() const;
@ -106,6 +112,8 @@ private slots:
void objcInObjcMode() const;
#endif
};
Q_DECLARE_OPERATORS_FOR_FLAGS(tst_QDebug::Flags)
Q_DECLARE_OPERATORS_FOR_FLAGS(tst_QDebug::Flags64)
void tst_QDebug::assignment() const
{
@ -1096,8 +1104,9 @@ void tst_QDebug::qDebugQFlags() const
QString file, function;
int line = 0;
QFlags<TestEnum> flags(Flag1 | Flag2 | SignFlag);
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;
@ -1108,17 +1117,54 @@ void tst_QDebug::qDebugQFlags() const
QCOMPARE(s_line, line);
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);
qDebug() << flags2;
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);
qDebug() << flags3;
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()>;
void tst_QDebug::qDebugStdChrono_data() const
{