Introduce QMetaObject::metaType
This function returns the metatype corresponding to the metaobject, or an invalid metatype for namespaces. This works as follows: First we increment the metaobject revision for new metaobjects. Metaobjects with older revisions are handled by doing a lookup by name. That fallback is also used for dynamic metaobjects (from QtDBUS and those created by QMetaObjectBuilder). For new metaobjects, we store the metatype in its metatype array, behind the property metatypes. This avoids any changes to the property and method metatype extraction logic: For properties, the metatype access does not change, as the new metatype is after their metatypes. For method metatypes, we already have an indirection layer (using offsets), so by adjusting those offsets by one, the same logic keeps working. To distinguish between namespaces and dynamic metaobjects, namespaces store the metatypeinterface pointer for void in the metatype array, whereas dynamic metaobjects store a nullptr. One nice additional benefit is that this simplifies the generator logic in moc, as the metatype array is now never empty. Task-number: QTBUG-92077 Change-Id: Id3f920f28553f12032a71a1a87dad29e5374dbe7 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io> Reviewed-by: Andrei Golubev <andrei.golubev@qt.io>
This commit is contained in:
parent
9145650302
commit
cb43aaca11
@ -384,6 +384,33 @@ QString QMetaObject::tr(const char *s, const char *c, int n) const
|
||||
{
|
||||
return QCoreApplication::translate(objectClassName(this), s, c, n);
|
||||
}
|
||||
|
||||
/*!
|
||||
\since 6.2
|
||||
Returns the metatype corresponding to this metaobject.
|
||||
If the metaobject originates from a namespace, an invalid metatype is returned.
|
||||
*/
|
||||
QMetaType QMetaObject::metaType() const
|
||||
{
|
||||
|
||||
const QMetaObjectPrivate *d = priv(this->d.data);
|
||||
if (d->revision < 10) {
|
||||
// before revision 10, we did not store the metatype in the metatype array
|
||||
return QMetaType::fromName(className());
|
||||
} else {
|
||||
/* in the metatype array, we store
|
||||
idx: 0 propertyCount - 1 propertyCount
|
||||
data:QMetaType(prop0), ..., QMetaType(propPropCount-1), QMetaType(class),...
|
||||
*/
|
||||
auto iface = this->d.metaTypes[d->propertyCount];
|
||||
if (iface == QtPrivate::qMetaTypeInterfaceForType<void>())
|
||||
return QMetaType(); // return invalid meta-type for namespaces
|
||||
if (iface)
|
||||
return QMetaType(iface);
|
||||
else // in case of a dynamic metaobject, we might have no metatype stored
|
||||
return QMetaType::fromName(className()); // try lookup by name in that case
|
||||
}
|
||||
}
|
||||
#endif // QT_NO_TRANSLATION
|
||||
|
||||
/*!
|
||||
|
@ -172,7 +172,7 @@ struct QMetaObjectPrivate
|
||||
// revision 7 is Qt 5.0 everything lower is not supported
|
||||
// revision 8 is Qt 5.12: It adds the enum name to QMetaEnum
|
||||
// revision 9 is Qt 6.0: It adds the metatype of properties and methods
|
||||
enum { OutputRevision = 9 }; // Used by moc, qmetaobjectbuilder and qdbus
|
||||
enum { OutputRevision = 10 }; // Used by moc, qmetaobjectbuilder and qdbus
|
||||
enum { IntsPerMethod = QMetaMethod::Data::Size };
|
||||
enum { IntsPerEnum = QMetaEnum::Data::Size };
|
||||
enum { IntsPerProperty = QMetaProperty::Data::Size };
|
||||
|
@ -1203,7 +1203,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 == 9, "QMetaObjectBuilder should generate the same version as moc");
|
||||
static_assert(QMetaObjectPrivate::OutputRevision == 10, "QMetaObjectBuilder should generate the same version as moc");
|
||||
pmeta->revision = QMetaObjectPrivate::OutputRevision;
|
||||
pmeta->flags = d->flags;
|
||||
pmeta->className = 0; // Class name is always the first string.
|
||||
@ -1281,7 +1281,8 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf,
|
||||
|
||||
// Output the methods in the class.
|
||||
Q_ASSERT(!buf || dataIndex == pmeta->methodData);
|
||||
int parameterMetaTypesIndex = int(d->properties.size());
|
||||
// + 1 for metatype of this metaobject
|
||||
int parameterMetaTypesIndex = int(d->properties.size()) + 1;
|
||||
for (const auto &method : d->methods) {
|
||||
[[maybe_unused]] int name = strings.enter(method.name());
|
||||
int argc = method.parameterCount();
|
||||
@ -1448,6 +1449,10 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf,
|
||||
*types = reinterpret_cast<QtPrivate::QMetaTypeInterface *&>(mt);
|
||||
types++;
|
||||
}
|
||||
// add metatype interface for this metaobject - must be null
|
||||
// as we can't know our metatype
|
||||
*types = nullptr;
|
||||
types++;
|
||||
for (const auto &method: d->methods) {
|
||||
QMetaType mt(QMetaType::fromName(method.returnType).id());
|
||||
*types = reinterpret_cast<QtPrivate::QMetaTypeInterface *&>(mt);
|
||||
|
@ -173,6 +173,8 @@ struct Q_CORE_EXPORT QMetaObject
|
||||
QString tr(const char *s, const char *c, int n = -1) const;
|
||||
#endif // QT_NO_TRANSLATION
|
||||
|
||||
QMetaType metaType() const;
|
||||
|
||||
int methodOffset() const;
|
||||
int enumeratorOffset() const;
|
||||
int propertyOffset() const;
|
||||
|
@ -422,7 +422,7 @@ void QDBusMetaObjectGenerator::write(QDBusMetaObject *obj)
|
||||
- methods.count(); // ditto
|
||||
|
||||
QDBusMetaObjectPrivate *header = reinterpret_cast<QDBusMetaObjectPrivate *>(idata.data());
|
||||
static_assert(QMetaObjectPrivate::OutputRevision == 9, "QtDBus meta-object generator should generate the same version as moc");
|
||||
static_assert(QMetaObjectPrivate::OutputRevision == 10, "QtDBus meta-object generator should generate the same version as moc");
|
||||
header->revision = QMetaObjectPrivate::OutputRevision;
|
||||
header->className = 0;
|
||||
header->classInfoCount = 0;
|
||||
@ -459,6 +459,7 @@ void QDBusMetaObjectGenerator::write(QDBusMetaObject *obj)
|
||||
idata[typeidOffset++] = 0; // eod
|
||||
|
||||
int totalMetaTypeCount = properties.count();
|
||||
++totalMetaTypeCount; // + 1 for metatype of dynamic metaobject
|
||||
for (const auto& methodContainer: {signals_, methods}) {
|
||||
for (const auto& method: methodContainer) {
|
||||
int argc = method.inputTypes.size() + qMax(qsizetype(0), method.outputTypes.size() - 1);
|
||||
@ -469,7 +470,7 @@ void QDBusMetaObjectGenerator::write(QDBusMetaObject *obj)
|
||||
int propertyId = 0;
|
||||
|
||||
// add each method:
|
||||
int currentMethodMetaTypeOffset = properties.count();
|
||||
int currentMethodMetaTypeOffset = properties.count() + 1;
|
||||
for (int x = 0; x < 2; ++x) {
|
||||
// Signals must be added before other methods, to match moc.
|
||||
QMap<QByteArray, Method> &map = (x == 0) ? signals_ : methods;
|
||||
@ -559,6 +560,7 @@ void QDBusMetaObjectGenerator::write(QDBusMetaObject *obj)
|
||||
|
||||
metaTypes[propertyId++] = QMetaType(mp.type);
|
||||
}
|
||||
metaTypes[propertyId] = QMetaType(); // we can't know our own metatype
|
||||
|
||||
Q_ASSERT(offset == header->propertyDBusData);
|
||||
Q_ASSERT(signatureOffset == header->methodDBusData);
|
||||
|
@ -396,7 +396,8 @@ void Generator::generateCode()
|
||||
//
|
||||
generateClassInfos();
|
||||
|
||||
int initialMetaTypeOffset = cdef->propertyList.count();
|
||||
// all property metatypes, + 1 for the type of the current class itself
|
||||
int initialMetaTypeOffset = cdef->propertyList.count() + 1;
|
||||
|
||||
//
|
||||
// Build signals array first, otherwise the signal indices would be wrong
|
||||
@ -560,64 +561,60 @@ void Generator::generateCode()
|
||||
else
|
||||
fprintf(out, " qt_meta_extradata_%s,\n", qualifiedClassNameIdentifier.constData());
|
||||
|
||||
bool constructorListContainsArgument = false;
|
||||
for (int i = 0; i< cdef->constructorList.count(); ++i) {
|
||||
const FunctionDef& fdef = cdef->constructorList.at(i);
|
||||
if (fdef.arguments.count()) {
|
||||
constructorListContainsArgument = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (cdef->propertyList.isEmpty() && cdef->signalList.isEmpty() && cdef->slotList.isEmpty() && cdef->methodList.isEmpty() && !constructorListContainsArgument) {
|
||||
fprintf(out, " nullptr,\n");
|
||||
bool needsComma = false;
|
||||
const bool requireCompleteness = requireCompleteTypes || cdef->requireCompleteMethodTypes;
|
||||
if (!requireCompleteness) {
|
||||
fprintf(out, "qt_incomplete_metaTypeArray<qt_meta_stringdata_%s_t\n", qualifiedClassNameIdentifier.constData());
|
||||
needsComma = true;
|
||||
} else {
|
||||
bool needsComma = false;
|
||||
const bool requireCompleteness = requireCompleteTypes || cdef->requireCompleteMethodTypes;
|
||||
if (!requireCompleteness) {
|
||||
fprintf(out, "qt_incomplete_metaTypeArray<qt_meta_stringdata_%s_t\n", qualifiedClassNameIdentifier.constData());
|
||||
needsComma = true;
|
||||
} else {
|
||||
fprintf(out, "qt_metaTypeArray<\n");
|
||||
}
|
||||
for (int i = 0; i < cdef->propertyList.count(); ++i) {
|
||||
const PropertyDef &p = cdef->propertyList.at(i);
|
||||
fprintf(out, "qt_metaTypeArray<\n");
|
||||
}
|
||||
// metatypes for properties
|
||||
for (int i = 0; i < cdef->propertyList.count(); ++i) {
|
||||
const PropertyDef &p = cdef->propertyList.at(i);
|
||||
if (requireCompleteness)
|
||||
fprintf(out, "%s%s", needsComma ? ", " : "", p.type.data());
|
||||
else
|
||||
fprintf(out, "%sQtPrivate::TypeAndForceComplete<%s, std::true_type>", needsComma ? ", " : "", p.type.data());
|
||||
needsComma = true;
|
||||
}
|
||||
// type name for the Q_OJBECT/GADGET itself, void for namespaces
|
||||
auto ownType = !cdef->hasQNamespace ? cdef->classname.data() : "void";
|
||||
if (requireCompleteness)
|
||||
fprintf(out, "%s%s", needsComma ? ", " : "", ownType);
|
||||
else
|
||||
fprintf(out, "%sQtPrivate::TypeAndForceComplete<%s, std::true_type>", needsComma ? ", " : "", ownType);
|
||||
|
||||
// metatypes for all exposed methods
|
||||
// no need to check for needsComma any longer, as we always need one due to the classname being present
|
||||
for (const QList<FunctionDef> &methodContainer :
|
||||
{ cdef->signalList, cdef->slotList, cdef->methodList }) {
|
||||
for (int i = 0; i< methodContainer.count(); ++i) {
|
||||
const FunctionDef& fdef = methodContainer.at(i);
|
||||
if (requireCompleteness)
|
||||
fprintf(out, "%s%s", needsComma ? ", " : "", p.type.data());
|
||||
fprintf(out, ", %s", fdef.type.name.data());
|
||||
else
|
||||
fprintf(out, "%sQtPrivate::TypeAndForceComplete<%s, std::true_type>", needsComma ? ", " : "", p.type.data());
|
||||
needsComma = true;
|
||||
}
|
||||
for (const QList<FunctionDef> &methodContainer :
|
||||
{ cdef->signalList, cdef->slotList, cdef->methodList }) {
|
||||
for (int i = 0; i< methodContainer.count(); ++i) {
|
||||
const FunctionDef& fdef = methodContainer.at(i);
|
||||
if (requireCompleteness)
|
||||
fprintf(out, "%s%s", needsComma ? ", " : "", fdef.type.name.data());
|
||||
else
|
||||
fprintf(out, "%sQtPrivate::TypeAndForceComplete<%s, std::false_type>", needsComma ? ", " : "", fdef.type.name.data());
|
||||
needsComma = true;
|
||||
for (const auto &argument: fdef.arguments) {
|
||||
if (requireCompleteness)
|
||||
fprintf(out, ", %s", argument.type.name.data());
|
||||
else
|
||||
fprintf(out, ", QtPrivate::TypeAndForceComplete<%s, std::false_type>", argument.type.name.data());
|
||||
}
|
||||
}
|
||||
fprintf(out, "\n");
|
||||
}
|
||||
for (int i = 0; i< cdef->constructorList.count(); ++i) {
|
||||
const FunctionDef& fdef = cdef->constructorList.at(i);
|
||||
fprintf(out, ", QtPrivate::TypeAndForceComplete<%s, std::false_type>", fdef.type.name.data());
|
||||
for (const auto &argument: fdef.arguments) {
|
||||
if (requireCompleteness)
|
||||
fprintf(out, "%s%s", needsComma ? ", " : "", argument.type.name.data());
|
||||
fprintf(out, ", %s", argument.type.name.data());
|
||||
else
|
||||
fprintf(out, "%sQtPrivate::TypeAndForceComplete<%s, std::false_type>", needsComma ? ", " : "", argument.type.name.data());
|
||||
needsComma = true;
|
||||
fprintf(out, ", QtPrivate::TypeAndForceComplete<%s, std::false_type>", argument.type.name.data());
|
||||
}
|
||||
}
|
||||
fprintf(out, "\n");
|
||||
fprintf(out, ">,\n");
|
||||
}
|
||||
for (int i = 0; i< cdef->constructorList.count(); ++i) {
|
||||
const FunctionDef& fdef = cdef->constructorList.at(i);
|
||||
for (const auto &argument: fdef.arguments) {
|
||||
if (requireCompleteness)
|
||||
fprintf(out, ", %s", argument.type.name.data());
|
||||
else
|
||||
fprintf(out, ", QtPrivate::TypeAndForceComplete<%s, std::false_type>", argument.type.name.data());
|
||||
}
|
||||
}
|
||||
fprintf(out, "\n");
|
||||
fprintf(out, ">,\n");
|
||||
|
||||
fprintf(out, " nullptr\n} };\n\n");
|
||||
|
||||
|
@ -315,6 +315,8 @@ private slots:
|
||||
void propertyConstant();
|
||||
void propertyFinal();
|
||||
|
||||
void metaType();
|
||||
|
||||
void stdSet();
|
||||
void classInfo();
|
||||
|
||||
@ -1612,6 +1614,15 @@ void tst_QMetaObject::propertyFinal()
|
||||
QVERIFY(!prop.isFinal());
|
||||
}
|
||||
|
||||
void tst_QMetaObject::metaType()
|
||||
{
|
||||
QCOMPARE(QObject::staticMetaObject.metaType(), QMetaType::fromType<QObject>());
|
||||
QCOMPARE(MyGadget::staticMetaObject.metaType(), QMetaType::fromType<MyGadget>());
|
||||
QCOMPARE(QAbstractProxyModel::staticMetaObject.metaType(), QMetaType::fromType<QAbstractProxyModel>());
|
||||
auto qtNameSpaceMetaType = Qt::staticMetaObject.metaType();
|
||||
QVERIFY2(!qtNameSpaceMetaType.isValid(), qtNameSpaceMetaType.name());
|
||||
}
|
||||
|
||||
class ClassInfoTestObjectA : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
Loading…
Reference in New Issue
Block a user