moc: Record types of enumerations

This will be helpful in a number of places, in particular in order to
support enums of different sizes in QML. We record the type as string in
the JSON output and as QMetaTypeInterface in the generated C++.

Task-number: QTBUG-112180
Change-Id: I943fac67f8b25b013d3860301416cdd293c0c69e
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
Ulf Hermann 2023-03-14 15:48:49 +01:00
parent ecd7ddcc3e
commit b83de5f9a4
13 changed files with 292 additions and 40 deletions

View File

@ -419,10 +419,30 @@ QMetaType QMetaObject::metaType() const
return QMetaType::fromName(className()); return QMetaType::fromName(className());
} else { } else {
/* in the metatype array, we store /* in the metatype array, we store
idx: 0 propertyCount - 1 propertyCount
data:QMetaType(prop0), ..., QMetaType(propPropCount-1), QMetaType(class),... | index | data |
|----------------------------------------------------------------------|
| 0 | QMetaType(property0) |
| ... | ... |
| propertyCount - 1 | QMetaType(propertyCount - 1) |
| propertyCount | QMetaType(enumerator0) |
| ... | ... |
| propertyCount + enumeratorCount - 1 | QMetaType(enumeratorCount - 1) |
| propertyCount + enumeratorCount | QMetaType(class) |
*/ */
auto iface = this->d.metaTypes[d->propertyCount]; #if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
// Before revision 12 we only stored metatypes for enums if they showed
// up as types of properties or method arguments or return values.
// From revision 12 on, we always store them in a predictable place.
const qsizetype offset = d->revision < 12
? d->propertyCount
: d->propertyCount + d->enumeratorCount;
#else
const qsizetype offset = d->propertyCount + d->enumeratorCount;
#endif
auto iface = this->d.metaTypes[offset];
if (iface && QtMetaTypePrivate::isInterfaceFor<void>(iface)) if (iface && QtMetaTypePrivate::isInterfaceFor<void>(iface))
return QMetaType(); // return invalid meta-type for namespaces return QMetaType(); // return invalid meta-type for namespaces
if (iface) if (iface)
@ -3036,6 +3056,33 @@ const char *QMetaEnum::enumName() const
return rawStringData(mobj, data.alias()); return rawStringData(mobj, data.alias());
} }
/*!
Returns the meta type of the enum.
If the QMetaObject this enum is part of was generated with Qt 6.5 or
earlier this will be the invalid metatype.
\note This is the meta type of the enum itself, not of its underlying
numeric type. You can retrieve the meta type of the underlying type of the
enum using \l{QMetaType::underlyingType()}.
\since 6.6
\sa QMetaType::underlyingType()
*/
QMetaType QMetaEnum::metaType() const
{
if (!mobj)
return {};
const QMetaObjectPrivate *p = priv(mobj->d.data);
#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
if (p->revision < 12)
QMetaType();
#endif
return QMetaType(mobj->d.metaTypes[data.index(mobj) + p->propertyCount]);
}
/*! /*!
Returns the number of keys. Returns the number of keys.
@ -3285,6 +3332,11 @@ QMetaEnum::QMetaEnum(const QMetaObject *mobj, int index)
Q_ASSERT(index >= 0 && index < priv(mobj->d.data)->enumeratorCount); Q_ASSERT(index >= 0 && index < priv(mobj->d.data)->enumeratorCount);
} }
int QMetaEnum::Data::index(const QMetaObject *mobj) const
{
return (d - mobj->d.data - priv(mobj->d.data)->enumeratorData) / Size;
}
/*! /*!
\fn QMetaEnum QMetaEnum::fromType() \fn QMetaEnum QMetaEnum::fromType()
\since 5.5 \since 5.5

View File

@ -265,6 +265,8 @@ public:
const char *name() const; const char *name() const;
const char *enumName() const; const char *enumName() const;
QMetaType metaType() const;
bool isFlag() const; bool isFlag() const;
bool isScoped() const; bool isScoped() const;
@ -302,6 +304,7 @@ private:
quint32 flags() const { return d[2]; } quint32 flags() const { return d[2]; }
qint32 keyCount() const { return static_cast<qint32>(d[3]); } qint32 keyCount() const { return static_cast<qint32>(d[3]); }
quint32 data() const { return d[4]; } quint32 data() const { return d[4]; }
int index(const QMetaObject *mobj) const;
const uint *d; const uint *d;
}; };

View File

@ -175,7 +175,8 @@ struct QMetaObjectPrivate
// revision 10 is Qt 6.2: The metatype of the metaobject is stored in the metatypes array // revision 10 is Qt 6.2: The metatype of the metaobject is stored in the metatypes array
// and metamethods store a flag stating whether they are const // and metamethods store a flag stating whether they are const
// revision 11 is Qt 6.5: The metatype for void is stored in the metatypes array // revision 11 is Qt 6.5: The metatype for void is stored in the metatypes array
enum { OutputRevision = 11 }; // Used by moc, qmetaobjectbuilder and qdbus // revision 12 is Qt 6.6: It adds the metatype for enums
enum { OutputRevision = 12 }; // Used by moc, qmetaobjectbuilder and qdbus
enum { IntsPerMethod = QMetaMethod::Data::Size }; enum { IntsPerMethod = QMetaMethod::Data::Size };
enum { IntsPerEnum = QMetaEnum::Data::Size }; enum { IntsPerEnum = QMetaEnum::Data::Size };
enum { IntsPerProperty = QMetaProperty::Data::Size }; enum { IntsPerProperty = QMetaProperty::Data::Size };

View File

@ -159,6 +159,7 @@ public:
QByteArray name; QByteArray name;
QByteArray enumName; QByteArray enumName;
QMetaType metaType;
bool isFlag; bool isFlag;
bool isScoped; bool isScoped;
QList<QByteArray> keys; QList<QByteArray> keys;
@ -597,6 +598,7 @@ QMetaEnumBuilder QMetaObjectBuilder::addEnumerator(const QMetaEnum &prototype)
{ {
QMetaEnumBuilder en = addEnumerator(prototype.name()); QMetaEnumBuilder en = addEnumerator(prototype.name());
en.setEnumName(prototype.enumName()); en.setEnumName(prototype.enumName());
en.setMetaType(prototype.metaType());
en.setIsFlag(prototype.isFlag()); en.setIsFlag(prototype.isFlag());
en.setIsScoped(prototype.isScoped()); en.setIsScoped(prototype.isScoped());
int count = prototype.keyCount(); int count = prototype.keyCount();
@ -1170,7 +1172,7 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf,
- int(d->methods.size()) // return "parameters" don't have names - int(d->methods.size()) // return "parameters" don't have names
- int(d->constructors.size()); // "this" parameters don't have names - int(d->constructors.size()); // "this" parameters don't have names
if constexpr (mode == Construct) { if constexpr (mode == Construct) {
static_assert(QMetaObjectPrivate::OutputRevision == 11, "QMetaObjectBuilder should generate the same version as moc"); static_assert(QMetaObjectPrivate::OutputRevision == 12, "QMetaObjectBuilder should generate the same version as moc");
pmeta->revision = QMetaObjectPrivate::OutputRevision; pmeta->revision = QMetaObjectPrivate::OutputRevision;
pmeta->flags = d->flags.toInt(); pmeta->flags = d->flags.toInt();
pmeta->className = 0; // Class name is always the first string. pmeta->className = 0; // Class name is always the first string.
@ -2305,6 +2307,31 @@ void QMetaEnumBuilder::setEnumName(const QByteArray &alias)
d->enumName = alias; d->enumName = alias;
} }
/*!
Returns the meta type of the enumerator.
\since 6.6
*/
QMetaType QMetaEnumBuilder::metaType() const
{
if (QMetaEnumBuilderPrivate *d = d_func())
return d->metaType;
return QMetaType();
}
/*!
Sets this enumerator to have the given \c metaType.
\since 6.6
\sa metaType()
*/
void QMetaEnumBuilder::setMetaType(QMetaType metaType)
{
QMetaEnumBuilderPrivate *d = d_func();
if (d)
d->metaType = metaType;
}
/*! /*!
Returns \c true if this enumerator is used as a flag; otherwise returns Returns \c true if this enumerator is used as a flag; otherwise returns
false. false.

View File

@ -258,6 +258,9 @@ public:
QByteArray enumName() const; QByteArray enumName() const;
void setEnumName(const QByteArray &alias); void setEnumName(const QByteArray &alias);
QMetaType metaType() const;
void setMetaType(QMetaType metaType);
bool isFlag() const; bool isFlag() const;
void setIsFlag(bool value); void setIsFlag(bool value);

View File

@ -391,7 +391,7 @@ void QDBusMetaObjectGenerator::write(QDBusMetaObject *obj)
- methods.size(); // ditto - methods.size(); // ditto
QDBusMetaObjectPrivate *header = reinterpret_cast<QDBusMetaObjectPrivate *>(idata.data()); QDBusMetaObjectPrivate *header = reinterpret_cast<QDBusMetaObjectPrivate *>(idata.data());
static_assert(QMetaObjectPrivate::OutputRevision == 11, "QtDBus meta-object generator should generate the same version as moc"); static_assert(QMetaObjectPrivate::OutputRevision == 12, "QtDBus meta-object generator should generate the same version as moc");
header->revision = QMetaObjectPrivate::OutputRevision; header->revision = QMetaObjectPrivate::OutputRevision;
header->className = 0; header->className = 0;
header->classInfoCount = 0; header->classInfoCount = 0;

View File

@ -378,7 +378,7 @@ void Generator::generateCode()
int enumsIndex = index; int enumsIndex = index;
for (int i = 0; i < cdef->enumList.size(); ++i) for (int i = 0; i < cdef->enumList.size(); ++i)
index += 5 + (cdef->enumList.at(i).values.size() * 2); index += QMetaObjectPrivate::IntsPerEnum + (cdef->enumList.at(i).values.size() * 2);
fprintf(out, " %4d, %4d, // constructors\n", isConstructible ? int(cdef->constructorList.size()) : 0, fprintf(out, " %4d, %4d, // constructors\n", isConstructible ? int(cdef->constructorList.size()) : 0,
isConstructible ? index : 0); isConstructible ? index : 0);
@ -397,8 +397,8 @@ void Generator::generateCode()
// //
generateClassInfos(); generateClassInfos();
// all property metatypes, + 1 for the type of the current class itself // all property metatypes + all enum metatypes + 1 for the type of the current class itself
int initialMetaTypeOffset = cdef->propertyList.size() + 1; int initialMetaTypeOffset = cdef->propertyList.size() + cdef->enumList.size() + 1;
// //
// Build signals array first, otherwise the signal indices would be wrong // Build signals array first, otherwise the signal indices would be wrong
@ -576,6 +576,15 @@ void Generator::generateCode()
comma = ","; comma = ",";
} }
// metatypes for enums
for (int i = 0; i < cdef->enumList.size(); ++i) {
const EnumDef &e = cdef->enumList.at(i);
fprintf(out, "%s\n // enum '%s'\n %s",
comma, e.name.constData(),
stringForType(cdef->classname % "::" % e.name, true).constData());
comma = ",";
}
// type name for the Q_OJBECT/GADGET itself, void for namespaces // type name for the Q_OJBECT/GADGET itself, void for namespaces
auto ownType = !cdef->hasQNamespace ? cdef->classname.data() : "void"; auto ownType = !cdef->hasQNamespace ? cdef->classname.data() : "void";
fprintf(out, "%s\n // Q_OBJECT / Q_GADGET\n %s", fprintf(out, "%s\n // Q_OBJECT / Q_GADGET\n %s",
@ -943,7 +952,7 @@ void Generator::generateEnums(int index)
return; return;
fprintf(out, "\n // enums: name, alias, flags, count, data\n"); fprintf(out, "\n // enums: name, alias, flags, count, data\n");
index += 5 * cdef->enumList.size(); index += QMetaObjectPrivate::IntsPerEnum * cdef->enumList.size();
int i; int i;
for (i = 0; i < cdef->enumList.size(); ++i) { for (i = 0; i < cdef->enumList.size(); ++i) {
const EnumDef &e = cdef->enumList.at(i); const EnumDef &e = cdef->enumList.at(i);

View File

@ -243,7 +243,7 @@ bool Moc::parseEnum(EnumDef *def)
} }
if (test(COLON)) { // C++11 strongly typed enum if (test(COLON)) { // C++11 strongly typed enum
// enum Foo : unsigned long { ... }; // enum Foo : unsigned long { ... };
parseType(); //ignore the result def->type = normalizeType(parseType().name);
} }
if (!test(LBRACE)) if (!test(LBRACE))
return false; return false;
@ -2107,6 +2107,8 @@ QJsonObject EnumDef::toJson(const ClassDef &cdef) const
def["name"_L1] = QString::fromUtf8(name); def["name"_L1] = QString::fromUtf8(name);
if (!enumName.isEmpty()) if (!enumName.isEmpty())
def["alias"_L1] = QString::fromUtf8(enumName); def["alias"_L1] = QString::fromUtf8(enumName);
if (!type.isEmpty())
def["type"_L1] = QString::fromUtf8(type);
def["isFlag"_L1] = cdef.enumDeclarations.value(name); def["isFlag"_L1] = cdef.enumDeclarations.value(name);
def["isClass"_L1] = isEnumClass; def["isClass"_L1] = isEnumClass;

View File

@ -43,6 +43,7 @@ struct EnumDef
{ {
QByteArray name; QByteArray name;
QByteArray enumName; QByteArray enumName;
QByteArray type;
QList<QByteArray> values; QList<QByteArray> values;
bool isEnumClass; // c++11 enum class bool isEnumClass; // c++11 enum class
EnumDef() : isEnumClass(false) {} EnumDef() : isEnumClass(false) {}

View File

@ -779,6 +779,26 @@ void tst_QMetaObjectBuilder::notifySignal()
void tst_QMetaObjectBuilder::enumerator() void tst_QMetaObjectBuilder::enumerator()
{ {
static const QtPrivate::QMetaTypeInterface fooFlagMetaType = {
0,
8,
8,
QMetaType::IsEnumeration | QMetaType::IsUnsignedEnumeration | QMetaType::RelocatableType,
{},
nullptr,
"fooFlag",
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
};
QMetaObjectBuilder builder; QMetaObjectBuilder builder;
// Add an enumerator and check its attributes. // Add an enumerator and check its attributes.
@ -810,6 +830,7 @@ void tst_QMetaObjectBuilder::enumerator()
enum1.setIsFlag(true); enum1.setIsFlag(true);
enum1.setIsScoped(true); enum1.setIsScoped(true);
enum1.setEnumName(QByteArrayLiteral("fooFlag")); enum1.setEnumName(QByteArrayLiteral("fooFlag"));
enum1.setMetaType(QMetaType(&fooFlagMetaType));
QCOMPARE(enum1.addKey("ABC", 0), 0); QCOMPARE(enum1.addKey("ABC", 0), 0);
QCOMPARE(enum1.addKey("DEF", 1), 1); QCOMPARE(enum1.addKey("DEF", 1), 1);
QCOMPARE(enum1.addKey("GHI", -1), 2); QCOMPARE(enum1.addKey("GHI", -1), 2);
@ -819,6 +840,7 @@ void tst_QMetaObjectBuilder::enumerator()
QVERIFY(enum1.isFlag()); QVERIFY(enum1.isFlag());
QVERIFY(enum1.isScoped()); QVERIFY(enum1.isScoped());
QCOMPARE(enum1.enumName(), QByteArray("fooFlag")); QCOMPARE(enum1.enumName(), QByteArray("fooFlag"));
QCOMPARE(enum1.metaType(), QMetaType(&fooFlagMetaType));
QCOMPARE(enum1.keyCount(), 3); QCOMPARE(enum1.keyCount(), 3);
QCOMPARE(enum1.index(), 0); QCOMPARE(enum1.index(), 0);
QCOMPARE(enum1.key(0), QByteArray("ABC")); QCOMPARE(enum1.key(0), QByteArray("ABC"));

View File

@ -91,6 +91,7 @@
"isClass": false, "isClass": false,
"isFlag": false, "isFlag": false,
"name": "TypedEnum", "name": "TypedEnum",
"type": "char",
"values": [ "values": [
"B0", "B0",
"B1", "B1",
@ -102,6 +103,7 @@
"isClass": true, "isClass": true,
"isFlag": false, "isFlag": false,
"name": "TypedEnumClass", "name": "TypedEnumClass",
"type": "char",
"values": [ "values": [
"C0", "C0",
"C1", "C1",
@ -147,6 +149,7 @@
"isClass": true, "isClass": true,
"isFlag": false, "isFlag": false,
"name": "TypedEnumStruct", "name": "TypedEnumStruct",
"type": "char",
"values": [ "values": [
"H0", "H0",
"H1", "H1",
@ -188,6 +191,7 @@
"isClass": false, "isClass": false,
"isFlag": false, "isFlag": false,
"name": "TypedEnum", "name": "TypedEnum",
"type": "char",
"values": [ "values": [
"B0", "B0",
"B1", "B1",
@ -199,6 +203,7 @@
"isClass": true, "isClass": true,
"isFlag": false, "isFlag": false,
"name": "TypedEnumClass", "name": "TypedEnumClass",
"type": "char",
"values": [ "values": [
"C0", "C0",
"C1", "C1",
@ -232,6 +237,65 @@
], ],
"gadget": true, "gadget": true,
"qualifiedClassName": "CXX11Enums2" "qualifiedClassName": "CXX11Enums2"
},
{
"className": "CXX11Enums3",
"enums": [
{
"isClass": true,
"isFlag": false,
"name": "EnumClass",
"values": [
"A0",
"A1",
"A2",
"A3"
]
},
{
"isClass": false,
"isFlag": false,
"name": "TypedEnum",
"type": "char",
"values": [
"B0",
"B1",
"B2",
"B3"
]
},
{
"isClass": true,
"isFlag": false,
"name": "TypedEnumClass",
"type": "char",
"values": [
"C0",
"C1",
"C2",
"C3"
]
},
{
"isClass": false,
"isFlag": false,
"name": "NormalEnum",
"values": [
"D2",
"D3",
"D0",
"D1"
]
}
],
"object": true,
"qualifiedClassName": "CXX11Enums3",
"superClasses": [
{
"access": "public",
"name": "QObject"
}
]
} }
], ],
"inputFile": "cxx11-enums.h", "inputFile": "cxx11-enums.h",

View File

@ -47,4 +47,20 @@ public:
Q_FLAGS(ClassFlags) Q_FLAGS(ClassFlags)
}; };
class CXX11Enums3 : public QObject
{
Q_OBJECT
public:
enum class EnumClass { A0, A1, A2, A3 };
enum TypedEnum : char { B0, B1 , B2, B3 };
enum class TypedEnumClass : char { C0, C1, C2, C3 };
enum NormalEnum { D2 = 2, D3, D0 =0 , D1 };
enum class ClassFlag { F0 = 1, F1 = 2, F2 = 4, F3 = 8 };
Q_ENUM(EnumClass)
Q_ENUM(TypedEnum)
Q_ENUM(TypedEnumClass)
Q_ENUM(NormalEnum)
};
#endif // CXX11_ENUMS_H #endif // CXX11_ENUMS_H

View File

@ -98,6 +98,12 @@ public:
Key2 Key2
}; };
Q_ENUM(TestGEnum2) Q_ENUM(TestGEnum2)
enum TestGEnum3: quint8 {
Key1 = 23,
Key2
};
Q_ENUM(TestGEnum3)
}; };
} }
@ -116,6 +122,12 @@ namespace TestQNamespace {
}; };
Q_ENUM_NS(TestEnum2) Q_ENUM_NS(TestEnum2)
enum TestEnum3: qint8 {
Key1 = 23,
Key2
};
Q_ENUM_NS(TestEnum3)
// try to dizzy moc by adding a struct in between // try to dizzy moc by adding a struct in between
struct TestGadget { struct TestGadget {
Q_GADGET Q_GADGET
@ -128,8 +140,13 @@ namespace TestQNamespace {
Key1 = 23, Key1 = 23,
Key2 Key2
}; };
enum TestGEnum3: qint16 {
Key1 = 33,
Key2
};
Q_ENUM(TestGEnum1) Q_ENUM(TestGEnum1)
Q_ENUM(TestGEnum2) Q_ENUM(TestGEnum2)
Q_ENUM(TestGEnum3)
}; };
struct TestGadgetExport { struct TestGadgetExport {
@ -146,6 +163,12 @@ namespace TestQNamespace {
Key2 Key2
}; };
Q_ENUM(TestGeEnum2) Q_ENUM(TestGeEnum2)
enum TestGeEnum3: quint16 {
Key1 = 26,
Key2
};
Q_ENUM(TestGeEnum3)
}; };
enum class TestFlag1 { enum class TestFlag1 {
@ -2428,23 +2451,29 @@ void tst_Moc::cxx11Enums_data()
QTest::addColumn<QByteArray>("enumName"); QTest::addColumn<QByteArray>("enumName");
QTest::addColumn<char>("prefix"); QTest::addColumn<char>("prefix");
QTest::addColumn<bool>("isScoped"); QTest::addColumn<bool>("isScoped");
QTest::addColumn<bool>("isTyped");
const QMetaObject *meta1 = &CXX11Enums::staticMetaObject; const QMetaObject *meta1 = &CXX11Enums::staticMetaObject;
const QMetaObject *meta2 = &CXX11Enums2::staticMetaObject; const QMetaObject *meta2 = &CXX11Enums2::staticMetaObject;
const QMetaObject *meta3 = &CXX11Enums3::staticMetaObject;
QTest::newRow("EnumClass") << meta1 << QByteArray("EnumClass") << QByteArray("EnumClass") << 'A' << true; QTest::newRow("EnumClass") << meta1 << QByteArray("EnumClass") << QByteArray("EnumClass") << 'A' << true << false;
QTest::newRow("EnumClass 2") << meta2 << QByteArray("EnumClass") << QByteArray("EnumClass") << 'A' << true; QTest::newRow("EnumClass 2") << meta2 << QByteArray("EnumClass") << QByteArray("EnumClass") << 'A' << true << false;
QTest::newRow("TypedEnum") << meta1 << QByteArray("TypedEnum") << QByteArray("TypedEnum") << 'B' << false; QTest::newRow("EnumClass 3") << meta3 << QByteArray("EnumClass") << QByteArray("EnumClass") << 'A' << true << false;
QTest::newRow("TypedEnum 2") << meta2 << QByteArray("TypedEnum") << QByteArray("TypedEnum") << 'B' << false; QTest::newRow("TypedEnum") << meta1 << QByteArray("TypedEnum") << QByteArray("TypedEnum") << 'B' << false << true;
QTest::newRow("TypedEnumClass") << meta1 << QByteArray("TypedEnumClass") << QByteArray("TypedEnumClass") << 'C' << true; QTest::newRow("TypedEnum 2") << meta2 << QByteArray("TypedEnum") << QByteArray("TypedEnum") << 'B' << false << true;
QTest::newRow("TypedEnumClass 2") << meta2 << QByteArray("TypedEnumClass") << QByteArray("TypedEnumClass") << 'C' << true; QTest::newRow("TypedEnum 3") << meta3 << QByteArray("TypedEnum") << QByteArray("TypedEnum") << 'B' << false << true;
QTest::newRow("NormalEnum") << meta1 << QByteArray("NormalEnum") << QByteArray("NormalEnum") << 'D' << false; QTest::newRow("TypedEnumClass") << meta1 << QByteArray("TypedEnumClass") << QByteArray("TypedEnumClass") << 'C' << true << true;
QTest::newRow("NormalEnum 2") << meta2 << QByteArray("NormalEnum") << QByteArray("NormalEnum") << 'D' << false; QTest::newRow("TypedEnumClass 2") << meta2 << QByteArray("TypedEnumClass") << QByteArray("TypedEnumClass") << 'C' << true << true;
QTest::newRow("ClassFlags") << meta1 << QByteArray("ClassFlags") << QByteArray("ClassFlag") << 'F' << true; QTest::newRow("TypedEnumClass 3") << meta3 << QByteArray("TypedEnumClass") << QByteArray("TypedEnumClass") << 'C' << true << true;
QTest::newRow("ClassFlags 2") << meta2 << QByteArray("ClassFlags") << QByteArray("ClassFlag") << 'F' << true; QTest::newRow("NormalEnum") << meta1 << QByteArray("NormalEnum") << QByteArray("NormalEnum") << 'D' << false << false;
QTest::newRow("EnumStruct") << meta1 << QByteArray("EnumStruct") << QByteArray("EnumStruct") << 'G' << true; QTest::newRow("NormalEnum 2") << meta2 << QByteArray("NormalEnum") << QByteArray("NormalEnum") << 'D' << false << false;
QTest::newRow("TypedEnumStruct") << meta1 << QByteArray("TypedEnumStruct") << QByteArray("TypedEnumStruct") << 'H' << true; QTest::newRow("NormalEnum 3") << meta3 << QByteArray("NormalEnum") << QByteArray("NormalEnum") << 'D' << false << false;
QTest::newRow("StructFlags") << meta1 << QByteArray("StructFlags") << QByteArray("StructFlag") << 'I' << true; QTest::newRow("ClassFlags") << meta1 << QByteArray("ClassFlags") << QByteArray("ClassFlag") << 'F' << true << false;
QTest::newRow("ClassFlags 2") << meta2 << QByteArray("ClassFlags") << QByteArray("ClassFlag") << 'F' << true << false;
QTest::newRow("EnumStruct") << meta1 << QByteArray("EnumStruct") << QByteArray("EnumStruct") << 'G' << true << false;
QTest::newRow("TypedEnumStruct") << meta1 << QByteArray("TypedEnumStruct") << QByteArray("TypedEnumStruct") << 'H' << true << true;
QTest::newRow("StructFlags") << meta1 << QByteArray("StructFlags") << QByteArray("StructFlag") << 'I' << true << false;
} }
void tst_Moc::cxx11Enums() void tst_Moc::cxx11Enums()
@ -2456,24 +2485,39 @@ void tst_Moc::cxx11Enums()
QFETCH(QByteArray, enumName); QFETCH(QByteArray, enumName);
QFETCH(char, prefix); QFETCH(char, prefix);
QFETCH(bool, isScoped); QFETCH(bool, isScoped);
QFETCH(bool, isTyped);
int idx = meta->indexOfEnumerator(typeName); int idx = meta->indexOfEnumerator(typeName);
QVERIFY(idx != -1); QVERIFY(idx != -1);
QCOMPARE(meta->indexOfEnumerator(enumName), idx); QCOMPARE(meta->indexOfEnumerator(enumName), idx);
QCOMPARE(meta->enumerator(idx).enclosingMetaObject(), meta); const QMetaEnum metaEnum = meta->enumerator(idx);
QCOMPARE(meta->enumerator(idx).isValid(), true); QCOMPARE(metaEnum.enclosingMetaObject(), meta);
QCOMPARE(meta->enumerator(idx).keyCount(), 4); QCOMPARE(metaEnum.isValid(), true);
QCOMPARE(meta->enumerator(idx).name(), typeName.constData()); QCOMPARE(metaEnum.keyCount(), 4);
QCOMPARE(meta->enumerator(idx).enumName(), enumName.constData()); QCOMPARE(metaEnum.name(), typeName.constData());
bool isFlag = meta->enumerator(idx).isFlag(); QCOMPARE(metaEnum.enumName(), enumName.constData());
const QMetaType metaType = metaEnum.metaType();
const bool isUnsigned = metaType.flags() & QMetaType::IsUnsignedEnumeration;
if (isTyped) {
QCOMPARE(metaType.sizeOf(), sizeof(char));
QCOMPARE(isUnsigned, !std::is_signed_v<char>);
} else if (isScoped) {
QCOMPARE(metaType.sizeOf(), sizeof(int));
QCOMPARE(isUnsigned, !std::is_signed_v<int>);
} else {
// underlying type is implementation defined
}
bool isFlag = metaEnum.isFlag();
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
QByteArray v = prefix + QByteArray::number(i); QByteArray v = prefix + QByteArray::number(i);
const int value = isFlag ? (1 << i) : i; const int value = isFlag ? (1 << i) : i;
QCOMPARE(meta->enumerator(idx).keyToValue(v), value); QCOMPARE(metaEnum.keyToValue(v), value);
QCOMPARE(meta->enumerator(idx).valueToKey(value), v.constData()); QCOMPARE(metaEnum.valueToKey(value), v.constData());
} }
QCOMPARE(meta->enumerator(idx).isScoped(), isScoped); QCOMPARE(metaEnum.isScoped(), isScoped);
} }
void tst_Moc::cxx11TrailingReturn() void tst_Moc::cxx11TrailingReturn()
@ -3994,10 +4038,12 @@ void tst_Moc::optionsFileError()
} }
static void checkEnum(const QMetaEnum &enumerator, const QByteArray &name, static void checkEnum(const QMetaEnum &enumerator, const QByteArray &name,
const QList<QPair<QByteArray, int>> &keys) const QList<QPair<QByteArray, int>> &keys,
const QMetaType underlyingType = QMetaType::fromType<int>())
{ {
QCOMPARE(name, QByteArray{enumerator.name()}); QCOMPARE(name, QByteArray{enumerator.name()});
QCOMPARE(keys.size(), enumerator.keyCount()); QCOMPARE(keys.size(), enumerator.keyCount());
QCOMPARE(underlyingType, enumerator.metaType().underlyingType());
for (int i = 0; i < enumerator.keyCount(); ++i) { for (int i = 0; i < enumerator.keyCount(); ++i) {
QCOMPARE(keys[i].first, QByteArray{enumerator.key(i)}); QCOMPARE(keys[i].first, QByteArray{enumerator.key(i)});
QCOMPARE(keys[i].second, enumerator.value(i)); QCOMPARE(keys[i].second, enumerator.value(i));
@ -4014,27 +4060,33 @@ public:
void tst_Moc::testQNamespace() void tst_Moc::testQNamespace()
{ {
QCOMPARE(TestQNamespace::staticMetaObject.enumeratorCount(), 4); QCOMPARE(TestQNamespace::staticMetaObject.enumeratorCount(), 5);
checkEnum(TestQNamespace::staticMetaObject.enumerator(0), "TestEnum1", checkEnum(TestQNamespace::staticMetaObject.enumerator(0), "TestEnum1",
{{"Key1", 11}, {"Key2", 12}}); {{"Key1", 11}, {"Key2", 12}});
checkEnum(TestQNamespace::staticMetaObject.enumerator(1), "TestEnum2", checkEnum(TestQNamespace::staticMetaObject.enumerator(1), "TestEnum2",
{{"Key1", 17}, {"Key2", 18}}); {{"Key1", 17}, {"Key2", 18}});
checkEnum(TestQNamespace::staticMetaObject.enumerator(2), "TestFlag1", checkEnum(TestQNamespace::staticMetaObject.enumerator(2), "TestEnum3",
{{"Key1", 23}, {"Key2", 24}}, QMetaType::fromType<qint8>());
checkEnum(TestQNamespace::staticMetaObject.enumerator(3), "TestFlag1",
{{"None", 0}, {"Flag1", 1}, {"Flag2", 2}, {"Any", 1 | 2}}); {{"None", 0}, {"Flag1", 1}, {"Flag2", 2}, {"Any", 1 | 2}});
checkEnum(TestQNamespace::staticMetaObject.enumerator(3), "TestFlag2", checkEnum(TestQNamespace::staticMetaObject.enumerator(4), "TestFlag2",
{{"None", 0}, {"Flag1", 4}, {"Flag2", 8}, {"Any", 4 | 8}}); {{"None", 0}, {"Flag1", 4}, {"Flag2", 8}, {"Any", 4 | 8}});
QCOMPARE(TestQNamespace::TestGadget::staticMetaObject.enumeratorCount(), 2); QCOMPARE(TestQNamespace::TestGadget::staticMetaObject.enumeratorCount(), 3);
checkEnum(TestQNamespace::TestGadget::staticMetaObject.enumerator(0), "TestGEnum1", checkEnum(TestQNamespace::TestGadget::staticMetaObject.enumerator(0), "TestGEnum1",
{{"Key1", 13}, {"Key2", 14}}); {{"Key1", 13}, {"Key2", 14}});
checkEnum(TestQNamespace::TestGadget::staticMetaObject.enumerator(1), "TestGEnum2", checkEnum(TestQNamespace::TestGadget::staticMetaObject.enumerator(1), "TestGEnum2",
{{"Key1", 23}, {"Key2", 24}}); {{"Key1", 23}, {"Key2", 24}});
checkEnum(TestQNamespace::TestGadget::staticMetaObject.enumerator(2), "TestGEnum3",
{{"Key1", 33}, {"Key2", 34}}, QMetaType::fromType<qint16>());
QCOMPARE(TestQNamespace::TestGadgetExport::staticMetaObject.enumeratorCount(), 2); QCOMPARE(TestQNamespace::TestGadgetExport::staticMetaObject.enumeratorCount(), 3);
checkEnum(TestQNamespace::TestGadgetExport::staticMetaObject.enumerator(0), "TestGeEnum1", checkEnum(TestQNamespace::TestGadgetExport::staticMetaObject.enumerator(0), "TestGeEnum1",
{{"Key1", 20}, {"Key2", 21}}); {{"Key1", 20}, {"Key2", 21}});
checkEnum(TestQNamespace::TestGadgetExport::staticMetaObject.enumerator(1), "TestGeEnum2", checkEnum(TestQNamespace::TestGadgetExport::staticMetaObject.enumerator(1), "TestGeEnum2",
{{"Key1", 23}, {"Key2", 24}}); {{"Key1", 23}, {"Key2", 24}});
checkEnum(TestQNamespace::TestGadgetExport::staticMetaObject.enumerator(2), "TestGeEnum3",
{{"Key1", 26}, {"Key2", 27}}, QMetaType::fromType<quint16>());
QMetaEnum meta = QMetaEnum::fromType<TestQNamespace::TestEnum1>(); QMetaEnum meta = QMetaEnum::fromType<TestQNamespace::TestEnum1>();
QVERIFY(meta.isValid()); QVERIFY(meta.isValid());