From e7cb3cb041dc11001cccf4307d1f9e46a2926821 Mon Sep 17 00:00:00 2001 From: Fabian Kosmale Date: Tue, 14 Mar 2023 14:59:20 +0100 Subject: [PATCH] QMetaType: Provide underlyingType for enums Currently, Qt assumes that enums always have int as their underlying type (both in QMetaEnum::keyToValue and in the QML engine). This change makes it possible to to retrieve the underlying type from an enum's metaype - or rather, a metatype of an integral type with the same size and signedness. The use cases aobve don't really rely on the exact same type. In most cases, we wouldn't even need the signedness, however that is already available anyway, and it will come in handy once QML supports bigint, and we need to decide whether we should return While it would be possible for individual users of this function to manually query the size and signedness, having a function returning a metatype offers additional convenience - especially in QML, where the conversion APIs generally operate on metatypes. Task-number: QTBUG-27451 Task-number: QTBUG-84055 Task-number: QTBUG-112180 Change-Id: Icf733b42df0ea64017d69f4d94cb7c855d9e3201 Reviewed-by: Ulf Hermann --- src/corelib/kernel/qmetatype.cpp | 54 +++++++++++++++++++ src/corelib/kernel/qmetatype.h | 2 + .../kernel/qmetatype/tst_qmetatype.cpp | 54 +++++++++++++++++++ .../corelib/kernel/qmetatype/tst_qmetatype.h | 2 + 4 files changed, 112 insertions(+) diff --git a/src/corelib/kernel/qmetatype.cpp b/src/corelib/kernel/qmetatype.cpp index 481d409debc..6c2fe77b40d 100644 --- a/src/corelib/kernel/qmetatype.cpp +++ b/src/corelib/kernel/qmetatype.cpp @@ -2896,6 +2896,59 @@ bool QMetaType::hasRegisteredDataStreamOperators() const return d_ptr && d_ptr->dataStreamIn != nullptr && d_ptr->dataStreamOut != nullptr; } +/*! + \since 6.6 + + If this metatype represents an enumeration, this method returns a + metatype of a numeric class of the same signedness and size as the + enums underlying type. + If it represents a QFlags type, it returns QMetaType::Int. + In all other cases an invalid QMetaType is returned. + */ +QMetaType QMetaType::underlyingType() const +{ + if (!d_ptr || !(flags() & IsEnumeration)) + return {}; + /* QFlags has enumeration set so that's handled here (qint32 + case), as QFlags uses int as the underlying type + Note that we do some approximation here, as we cannot + differentiate between different underlying types of the + same size and signedness (consider char <-> (un)signed char, + int <-> long <-> long long). + + ### TODO PENDING: QTBUG-111926 - QFlags supporting >32 bit int + */ + if (flags() & IsUnsignedEnumeration) { + switch (sizeOf()) { + case 1: + return QMetaType::fromType(); + case 2: + return QMetaType::fromType(); + case 4: + return QMetaType::fromType(); + case 8: + return QMetaType::fromType(); + default: + break; + } + } else { + switch (sizeOf()) { + case 1: + return QMetaType::fromType(); + case 2: + return QMetaType::fromType(); + case 4: + return QMetaType::fromType(); + case 8: + return QMetaType::fromType(); + default: + break; + } + } + // int128 can be handled above once we have qint128 + return QMetaType(); +} + /*! \fn bool QMetaType::load(QDataStream &stream, int type, void *data) \overload @@ -3163,6 +3216,7 @@ QT_FOR_EACH_STATIC_PRIMITIVE_POINTER(QT_METATYPE_DECLARE_TEMPLATE_ITER) QT_FOR_EACH_STATIC_CORE_CLASS(QT_METATYPE_DECLARE_TEMPLATE_ITER) QT_FOR_EACH_STATIC_CORE_POINTER(QT_METATYPE_DECLARE_TEMPLATE_ITER) QT_FOR_EACH_STATIC_CORE_TEMPLATE(QT_METATYPE_DECLARE_TEMPLATE_ITER) + #undef QT_METATYPE_DECLARE_TEMPLATE_ITER #endif } diff --git a/src/corelib/kernel/qmetatype.h b/src/corelib/kernel/qmetatype.h index 54341170d16..da1159223df 100644 --- a/src/corelib/kernel/qmetatype.h +++ b/src/corelib/kernel/qmetatype.h @@ -485,6 +485,8 @@ public: #endif #endif + QMetaType underlyingType() const; + template constexpr static QMetaType fromType(); static QMetaType fromName(QByteArrayView name); diff --git a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp index f0efff34365..61ea16c4da0 100644 --- a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp +++ b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp @@ -13,6 +13,8 @@ #include #include +#include + Q_DECLARE_METATYPE(QMetaType::Type) Q_DECLARE_METATYPE(QPartialOrdering) @@ -1838,6 +1840,58 @@ void tst_QMetaType::isEnum() QVERIFY((QMetaType(type6).flags() & QMetaType::IsEnumeration) == QMetaType::IsEnumeration); } +enum E1 : unsigned char {}; +enum E2 : qlonglong {}; +enum class E3 : unsigned short {}; + +namespace myflags { + + Q_NAMESPACE + + enum Flag1 : int { A, B }; + enum Flag2 : short { X, Y }; + + Q_DECLARE_FLAGS(Flags1, myflags::Flag1); + Q_FLAG_NS(Flags1) + Q_DECLARE_FLAGS(Flags2, myflags::Flag2); + Q_FLAG_NS(Flags2) + +} + +template +using getUnderlyingTypeNormalized = std::conditional_t< + std::is_signed_v>, + typename QIntegerForSize::Signed, + typename QIntegerForSize::Unsigned +>; + +void tst_QMetaType::underlyingType_data() +{ + QTest::addColumn("source"); + QTest::addColumn("underlying"); + + QTest::newRow("invalid") << QMetaType() << QMetaType(); + QTest::newRow("plain") << QMetaType::fromType() + << QMetaType::fromType>(); + QTest::newRow("uchar") << QMetaType::fromType() + << QMetaType::fromType>(); + QTest::newRow("long") << QMetaType::fromType() + << QMetaType::fromType>(); + QTest::newRow("class_ushort") << QMetaType::fromType() + << QMetaType::fromType>(); + QTest::newRow("flags_int") << QMetaType::fromType() + << QMetaType::fromType(); + QTest::newRow("flags_short") << QMetaType::fromType() + << QMetaType::fromType(); // sic, not short! +} + +void tst_QMetaType::underlyingType() +{ + QFETCH(QMetaType, source); + QFETCH(QMetaType, underlying); + QCOMPARE(source.underlyingType(), underlying); +} + void tst_QMetaType::isRegisteredStaticLess_data() { isRegistered_data(); diff --git a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.h b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.h index 75bb3635492..3afb4943682 100644 --- a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.h +++ b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.h @@ -91,6 +91,8 @@ private slots: void isRegisteredStaticLess(); void isNotRegistered(); void isEnum(); + void underlyingType_data(); + void underlyingType(); void automaticTemplateRegistration_1(); void automaticTemplateRegistration_2(); // defined in tst_qmetatype3.cpp void saveAndLoadBuiltin_data();