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());
} else {
/* 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))
return QMetaType(); // return invalid meta-type for namespaces
if (iface)
@ -3036,6 +3056,33 @@ const char *QMetaEnum::enumName() const
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.
@ -3285,6 +3332,11 @@ QMetaEnum::QMetaEnum(const QMetaObject *mobj, int index)
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()
\since 5.5

View File

@ -265,6 +265,8 @@ public:
const char *name() const;
const char *enumName() const;
QMetaType metaType() const;
bool isFlag() const;
bool isScoped() const;
@ -302,6 +304,7 @@ private:
quint32 flags() const { return d[2]; }
qint32 keyCount() const { return static_cast<qint32>(d[3]); }
quint32 data() const { return d[4]; }
int index(const QMetaObject *mobj) const;
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
// 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
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 { IntsPerEnum = QMetaEnum::Data::Size };
enum { IntsPerProperty = QMetaProperty::Data::Size };

View File

@ -159,6 +159,7 @@ public:
QByteArray name;
QByteArray enumName;
QMetaType metaType;
bool isFlag;
bool isScoped;
QList<QByteArray> keys;
@ -597,6 +598,7 @@ QMetaEnumBuilder QMetaObjectBuilder::addEnumerator(const QMetaEnum &prototype)
{
QMetaEnumBuilder en = addEnumerator(prototype.name());
en.setEnumName(prototype.enumName());
en.setMetaType(prototype.metaType());
en.setIsFlag(prototype.isFlag());
en.setIsScoped(prototype.isScoped());
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->constructors.size()); // "this" parameters don't have names
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->flags = d->flags.toInt();
pmeta->className = 0; // Class name is always the first string.
@ -2305,6 +2307,31 @@ void QMetaEnumBuilder::setEnumName(const QByteArray &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
false.

View File

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

View File

@ -391,7 +391,7 @@ void QDBusMetaObjectGenerator::write(QDBusMetaObject *obj)
- methods.size(); // ditto
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->className = 0;
header->classInfoCount = 0;

View File

@ -378,7 +378,7 @@ void Generator::generateCode()
int enumsIndex = index;
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,
isConstructible ? index : 0);
@ -397,8 +397,8 @@ void Generator::generateCode()
//
generateClassInfos();
// all property metatypes, + 1 for the type of the current class itself
int initialMetaTypeOffset = cdef->propertyList.size() + 1;
// all property metatypes + all enum metatypes + 1 for the type of the current class itself
int initialMetaTypeOffset = cdef->propertyList.size() + cdef->enumList.size() + 1;
//
// Build signals array first, otherwise the signal indices would be wrong
@ -576,6 +576,15 @@ void Generator::generateCode()
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
auto ownType = !cdef->hasQNamespace ? cdef->classname.data() : "void";
fprintf(out, "%s\n // Q_OBJECT / Q_GADGET\n %s",
@ -943,7 +952,7 @@ void Generator::generateEnums(int index)
return;
fprintf(out, "\n // enums: name, alias, flags, count, data\n");
index += 5 * cdef->enumList.size();
index += QMetaObjectPrivate::IntsPerEnum * cdef->enumList.size();
int i;
for (i = 0; i < cdef->enumList.size(); ++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
// enum Foo : unsigned long { ... };
parseType(); //ignore the result
def->type = normalizeType(parseType().name);
}
if (!test(LBRACE))
return false;
@ -2107,6 +2107,8 @@ QJsonObject EnumDef::toJson(const ClassDef &cdef) const
def["name"_L1] = QString::fromUtf8(name);
if (!enumName.isEmpty())
def["alias"_L1] = QString::fromUtf8(enumName);
if (!type.isEmpty())
def["type"_L1] = QString::fromUtf8(type);
def["isFlag"_L1] = cdef.enumDeclarations.value(name);
def["isClass"_L1] = isEnumClass;

View File

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

View File

@ -779,6 +779,26 @@ void tst_QMetaObjectBuilder::notifySignal()
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;
// Add an enumerator and check its attributes.
@ -810,6 +830,7 @@ void tst_QMetaObjectBuilder::enumerator()
enum1.setIsFlag(true);
enum1.setIsScoped(true);
enum1.setEnumName(QByteArrayLiteral("fooFlag"));
enum1.setMetaType(QMetaType(&fooFlagMetaType));
QCOMPARE(enum1.addKey("ABC", 0), 0);
QCOMPARE(enum1.addKey("DEF", 1), 1);
QCOMPARE(enum1.addKey("GHI", -1), 2);
@ -819,6 +840,7 @@ void tst_QMetaObjectBuilder::enumerator()
QVERIFY(enum1.isFlag());
QVERIFY(enum1.isScoped());
QCOMPARE(enum1.enumName(), QByteArray("fooFlag"));
QCOMPARE(enum1.metaType(), QMetaType(&fooFlagMetaType));
QCOMPARE(enum1.keyCount(), 3);
QCOMPARE(enum1.index(), 0);
QCOMPARE(enum1.key(0), QByteArray("ABC"));

View File

@ -91,6 +91,7 @@
"isClass": false,
"isFlag": false,
"name": "TypedEnum",
"type": "char",
"values": [
"B0",
"B1",
@ -102,6 +103,7 @@
"isClass": true,
"isFlag": false,
"name": "TypedEnumClass",
"type": "char",
"values": [
"C0",
"C1",
@ -147,6 +149,7 @@
"isClass": true,
"isFlag": false,
"name": "TypedEnumStruct",
"type": "char",
"values": [
"H0",
"H1",
@ -188,6 +191,7 @@
"isClass": false,
"isFlag": false,
"name": "TypedEnum",
"type": "char",
"values": [
"B0",
"B1",
@ -199,6 +203,7 @@
"isClass": true,
"isFlag": false,
"name": "TypedEnumClass",
"type": "char",
"values": [
"C0",
"C1",
@ -232,6 +237,65 @@
],
"gadget": true,
"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",

View File

@ -47,4 +47,20 @@ public:
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

View File

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