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 <ulf.hermann@qt.io>
This commit is contained in:
Fabian Kosmale 2023-03-14 14:59:20 +01:00
parent 454b1de22c
commit e7cb3cb041
4 changed files with 112 additions and 0 deletions

View File

@ -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<quint8>();
case 2:
return QMetaType::fromType<quint16>();
case 4:
return QMetaType::fromType<quint32>();
case 8:
return QMetaType::fromType<quint64>();
default:
break;
}
} else {
switch (sizeOf()) {
case 1:
return QMetaType::fromType<qint8>();
case 2:
return QMetaType::fromType<qint16>();
case 4:
return QMetaType::fromType<qint32>();
case 8:
return QMetaType::fromType<qint64>();
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
}

View File

@ -485,6 +485,8 @@ public:
#endif
#endif
QMetaType underlyingType() const;
template<typename T>
constexpr static QMetaType fromType();
static QMetaType fromName(QByteArrayView name);

View File

@ -13,6 +13,8 @@
#include <memory>
#include <vector>
#include <QtCore/qflags.h>
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 <typename T>
using getUnderlyingTypeNormalized = std::conditional_t<
std::is_signed_v<std::underlying_type_t<T>>,
typename QIntegerForSize<sizeof(T)>::Signed,
typename QIntegerForSize<sizeof(T)>::Unsigned
>;
void tst_QMetaType::underlyingType_data()
{
QTest::addColumn<QMetaType>("source");
QTest::addColumn<QMetaType>("underlying");
QTest::newRow("invalid") << QMetaType() << QMetaType();
QTest::newRow("plain") << QMetaType::fromType<isEnumTest_Enum1>()
<< QMetaType::fromType<getUnderlyingTypeNormalized<isEnumTest_Enum1>>();
QTest::newRow("uchar") << QMetaType::fromType<E1>()
<< QMetaType::fromType<getUnderlyingTypeNormalized<E1>>();
QTest::newRow("long") << QMetaType::fromType<E2>()
<< QMetaType::fromType<getUnderlyingTypeNormalized<E2>>();
QTest::newRow("class_ushort") << QMetaType::fromType<E3>()
<< QMetaType::fromType<getUnderlyingTypeNormalized<E3>>();
QTest::newRow("flags_int") << QMetaType::fromType<myflags::Flags1>()
<< QMetaType::fromType<int>();
QTest::newRow("flags_short") << QMetaType::fromType<myflags::Flags2>()
<< QMetaType::fromType<int>(); // sic, not short!
}
void tst_QMetaType::underlyingType()
{
QFETCH(QMetaType, source);
QFETCH(QMetaType, underlying);
QCOMPARE(source.underlyingType(), underlying);
}
void tst_QMetaType::isRegisteredStaticLess_data()
{
isRegistered_data();

View File

@ -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();