From 74fac865cf7c55d3afba1043072ba6cc932d161d Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Tue, 19 Jul 2022 12:31:07 -0700 Subject: [PATCH] QMetaType: add registerType() and qRegisterMetaType(QMetaType) This also rewrites QMetaType::id() on top of the helper, with the benefit of calling a member static function, so QMetaType doesn't need to be spilled onto the stack. In some upcoming changes I need to ensure that QMetaTypes are registered so they can be found by name and I'd like to have a dedicated function name for that, instead of calling .id(). Since I needed to add docs for the new function, I've updated for the old one too. [ChangeLog][QMetaType] Added QMetaType::registerType() and an overload of qRegisterMetaType() taking QMetaType (the two functions do the same thing). These two functions ensure a given QMetaType is registered with the Qt global registry, so they can be found by name later. Using qRegisterMetaType() also accomplishes the same thing, but is slightly better for completely generic code because it will avoid emitting the registration for built-in types. Change-Id: I3859764fed084846bcb0fffd170351d606034c22 Reviewed-by: Qt CI Bot Reviewed-by: Fabian Kosmale --- src/corelib/compat/removed_api.cpp | 8 +++ src/corelib/kernel/qmetatype.cpp | 68 ++++++++++++++----- src/corelib/kernel/qmetatype.h | 36 ++++++++-- .../kernel/qmetatype/tst_qmetatype.cpp | 24 +++++++ 4 files changed, 112 insertions(+), 24 deletions(-) diff --git a/src/corelib/compat/removed_api.cpp b/src/corelib/compat/removed_api.cpp index c4fe85f84fd..7ffefa027ef 100644 --- a/src/corelib/compat/removed_api.cpp +++ b/src/corelib/compat/removed_api.cpp @@ -283,6 +283,14 @@ QT_WARNING_POP #if QT_CORE_REMOVED_SINCE(6, 5) +#include "qmetatype.h" + +int QMetaType::idHelper() const +{ + Q_ASSERT(d_ptr); + return registerHelper(d_ptr); +} + #include "qxmlstream.h" QXmlStreamReader::QXmlStreamReader(const QByteArray &data) diff --git a/src/corelib/kernel/qmetatype.cpp b/src/corelib/kernel/qmetatype.cpp index 4aed2edebb8..ea7cd4a0c9b 100644 --- a/src/corelib/kernel/qmetatype.cpp +++ b/src/corelib/kernel/qmetatype.cpp @@ -495,19 +495,28 @@ bool QMetaType::isRegistered() const \fn int QMetaType::id() const \since 5.13 - Returns id type hold by this QMetatype instance. + Returns id type held by this QMetatype instance. */ +/*! + \fn void QMetaType::registerType() const + \since 6.5 + + Registers this QMetaType with the type registry so it can be found by name, + using QMetaType::fromName(). + + \sa qRegisterMetaType() + */ /*! \internal - The slowpath of id(). Precondition: d_ptr != nullptr -*/ -int QMetaType::idHelper() const + Out-of-line path for registerType() and slow path id(). + */ +int QMetaType::registerHelper(const QtPrivate::QMetaTypeInterface *iface) { - Q_ASSERT(d_ptr); + Q_ASSERT(iface); auto reg = customTypeRegistry(); if (reg) { - return reg->registerCustomType(d_ptr); + return reg->registerCustomType(iface); } return 0; } @@ -2890,6 +2899,7 @@ QMetaType QMetaType::fromName(QByteArrayView typeName) /*! \fn int qRegisterMetaType(const char *typeName) \relates QMetaType + \obsolete \threadsafe Registers the type name \a typeName for the type \c{T}. Returns @@ -2926,8 +2936,7 @@ QMetaType QMetaType::fromName(QByteArrayView typeName) \threadsafe \since 4.2 - Call this function to register the type \c T. \c T must be declared with - Q_DECLARE_METATYPE(). Returns the meta type Id. + Call this function to register the type \c T. Returns the meta type Id. Example: @@ -2938,22 +2947,45 @@ QMetaType QMetaType::fromName(QByteArrayView typeName) pointed to type is fully defined. Use Q_DECLARE_OPAQUE_POINTER() to be able to register pointers to forward declared types. - After a type has been registered, you can create and destroy - objects of that type dynamically at run-time. + To use the type \c T in QMetaType, QVariant, or with the + QObject::property() API, registration is not necessary. - To use the type \c T in QVariant, using Q_DECLARE_METATYPE() is - sufficient. To use the type \c T in queued signal and slot connections, - \c{qRegisterMetaType()} must be called before the first connection - is established. + To use the type \c T in queued signal and slot connections, + \c{qRegisterMetaType()} must be called before the first connection is + established. That is typically done in the constructor of the class that + uses \c T, or in the \c{main()} function. - Also, to use type \c T with the QObject::property() API, - \c{qRegisterMetaType()} must be called before it is used, typically - in the constructor of the class that uses \c T, or in the \c{main()} - function. + After a type has been registered, it can be found by its name using + QMetaType::fromName(). \sa Q_DECLARE_METATYPE() */ +/*! + \fn int qRegisterMetaType(QMetaType meta) + \relates QMetaType + \threadsafe + \since 6.5 + + Registers the meta type \a meta and returns its type Id. + + This function requires that \c{T} is a fully defined type at the point + where the function is called. For pointer types, it also requires that the + pointed to type is fully defined. Use Q_DECLARE_OPAQUE_POINTER() to be able + to register pointers to forward declared types. + + To use the type \c T in QMetaType, QVariant, or with the + QObject::property() API, registration is not necessary. + + To use the type \c T in queued signal and slot connections, + \c{qRegisterMetaType()} must be called before the first connection is + established. That is typically done in the constructor of the class that + uses \c T, or in the \c{main()} function. + + After a type has been registered, it can be found by its name using + QMetaType::fromName(). + */ + /*! \fn int qMetaTypeId() \relates QMetaType diff --git a/src/corelib/kernel/qmetatype.h b/src/corelib/kernel/qmetatype.h index f4d3c5b54d7..e9599af5bbc 100644 --- a/src/corelib/kernel/qmetatype.h +++ b/src/corelib/kernel/qmetatype.h @@ -426,6 +426,11 @@ public: bool isValid() const; bool isRegistered() const; + void registerType() const + { + // "register" is a reserved keyword + registerHelper(); + } #if QT_CORE_REMOVED_SINCE(6, 1) || defined(Q_QDOC) int id() const; #else @@ -434,12 +439,7 @@ public: int id(int = 0) const { // keep in sync with the version in removed_api.cpp - if (d_ptr) { - if (int id = d_ptr->typeId.loadRelaxed()) - return id; - return idHelper(); - } - return 0; + return registerHelper(); } #endif constexpr qsizetype sizeOf() const; @@ -722,7 +722,23 @@ public: const QtPrivate::QMetaTypeInterface *iface() const { return d_ptr; } private: +#if QT_CORE_REMOVED_SINCE(6, 5) int idHelper() const; +#endif + static int registerHelper(const QtPrivate::QMetaTypeInterface *iface); + int registerHelper() const + { + // keep in sync with the QMetaType::id() version in removed_api.cpp + if (d_ptr) { + if (int id = d_ptr->typeId.loadRelaxed()) + return id; + return registerHelper(d_ptr); + } + return 0; + } + + friend int qRegisterMetaType(QMetaType meta); + friend class QVariant; const QtPrivate::QMetaTypeInterface *d_ptr = nullptr; }; @@ -1285,6 +1301,9 @@ template inline constexpr int qMetaTypeId() { if constexpr (bool(QMetaTypeId2::IsBuiltIn)) { + // this has the same result as the below code, but avoids asking the + // compiler to load a global variable whose value we know at compile + // time return QMetaTypeId2::MetaType; } else { return QMetaType::fromType().id(); @@ -1298,6 +1317,11 @@ inline constexpr int qRegisterMetaType() return id; } +inline int qRegisterMetaType(QMetaType meta) +{ + return meta.registerHelper(); +} + #ifndef QT_NO_QOBJECT template struct QMetaTypeIdQObject diff --git a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp index 9a3ab830cb8..c25c5c6b16f 100644 --- a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp +++ b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp @@ -1424,20 +1424,27 @@ void tst_QMetaType::typedefs() QCOMPARE(QMetaType::type("WhityDouble"), ::qMetaTypeId()); } +struct RegisterTypeType {}; + void tst_QMetaType::registerType() { // Built-in QCOMPARE(qRegisterMetaType("QString"), int(QMetaType::QString)); QCOMPARE(qRegisterMetaType("QString"), int(QMetaType::QString)); + qRegisterMetaType(QMetaType::fromType()); // Custom int fooId = qRegisterMetaType("TestSpace::Foo"); QVERIFY(fooId >= int(QMetaType::User)); QCOMPARE(qRegisterMetaType("TestSpace::Foo"), fooId); + qRegisterMetaType(QMetaType::fromType()); int movableId = qRegisterMetaType("CustomMovable"); QVERIFY(movableId >= int(QMetaType::User)); QCOMPARE(qRegisterMetaType("CustomMovable"), movableId); + qRegisterMetaType(QMetaType::fromType()); + + // Aliases are deprecated // Alias to built-in typedef QString MyString; @@ -1460,6 +1467,23 @@ void tst_QMetaType::registerType() QCOMPARE(qRegisterMetaType("MyFoo"), fooId); QCOMPARE(QMetaType::type("MyFoo"), fooId); + + // this portion of the test can only be run once + static bool typeWasRegistered = false; + if (!typeWasRegistered) { + QMetaType mt = QMetaType::fromType(); + QVERIFY(mt.isValid()); + QCOMPARE_NE(mt.name(), nullptr); + + QVERIFY(!mt.isRegistered()); + QVERIFY(!QMetaType::fromName(mt.name()).isValid()); + + QCOMPARE_GT(qRegisterMetaType(mt), 0); + typeWasRegistered = true; + + QVERIFY(mt.isRegistered()); + QVERIFY(QMetaType::fromName(mt.name()).isValid()); + } } class IsRegisteredDummyType { };