diff --git a/src/corelib/kernel/qmetatype.cpp b/src/corelib/kernel/qmetatype.cpp index 481d409deb..6c2fe77b40 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 54341170d1..da1159223d 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 f0efff3436..61ea16c4da 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 75bb363549..3afb494368 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();