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<T>() 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 <qt_ci_bot@qt-project.org>
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
Thiago Macieira 2022-07-19 12:31:07 -07:00
parent b1d9331c15
commit 74fac865cf
4 changed files with 112 additions and 24 deletions

View File

@ -283,6 +283,14 @@ QT_WARNING_POP
#if QT_CORE_REMOVED_SINCE(6, 5) #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" #include "qxmlstream.h"
QXmlStreamReader::QXmlStreamReader(const QByteArray &data) QXmlStreamReader::QXmlStreamReader(const QByteArray &data)

View File

@ -495,19 +495,28 @@ bool QMetaType::isRegistered() const
\fn int QMetaType::id() const \fn int QMetaType::id() const
\since 5.13 \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 \internal
The slowpath of id(). Precondition: d_ptr != nullptr Out-of-line path for registerType() and slow path id().
*/ */
int QMetaType::idHelper() const int QMetaType::registerHelper(const QtPrivate::QMetaTypeInterface *iface)
{ {
Q_ASSERT(d_ptr); Q_ASSERT(iface);
auto reg = customTypeRegistry(); auto reg = customTypeRegistry();
if (reg) { if (reg) {
return reg->registerCustomType(d_ptr); return reg->registerCustomType(iface);
} }
return 0; return 0;
} }
@ -2890,6 +2899,7 @@ QMetaType QMetaType::fromName(QByteArrayView typeName)
/*! /*!
\fn int qRegisterMetaType(const char *typeName) \fn int qRegisterMetaType(const char *typeName)
\relates QMetaType \relates QMetaType
\obsolete
\threadsafe \threadsafe
Registers the type name \a typeName for the type \c{T}. Returns Registers the type name \a typeName for the type \c{T}. Returns
@ -2926,8 +2936,7 @@ QMetaType QMetaType::fromName(QByteArrayView typeName)
\threadsafe \threadsafe
\since 4.2 \since 4.2
Call this function to register the type \c T. \c T must be declared with Call this function to register the type \c T. Returns the meta type Id.
Q_DECLARE_METATYPE(). Returns the meta type Id.
Example: Example:
@ -2938,22 +2947,45 @@ QMetaType QMetaType::fromName(QByteArrayView typeName)
pointed to type is fully defined. Use Q_DECLARE_OPAQUE_POINTER() to be able pointed to type is fully defined. Use Q_DECLARE_OPAQUE_POINTER() to be able
to register pointers to forward declared types. to register pointers to forward declared types.
After a type has been registered, you can create and destroy To use the type \c T in QMetaType, QVariant, or with the
objects of that type dynamically at run-time. QObject::property() API, registration is not necessary.
To use the type \c T in QVariant, using Q_DECLARE_METATYPE() is To use the type \c T in queued signal and slot connections,
sufficient. To use the type \c T in queued signal and slot connections, \c{qRegisterMetaType<T>()} must be called before the first connection is
\c{qRegisterMetaType<T>()} must be called before the first connection established. That is typically done in the constructor of the class that
is established. uses \c T, or in the \c{main()} function.
Also, to use type \c T with the QObject::property() API, After a type has been registered, it can be found by its name using
\c{qRegisterMetaType<T>()} must be called before it is used, typically QMetaType::fromName().
in the constructor of the class that uses \c T, or in the \c{main()}
function.
\sa Q_DECLARE_METATYPE() \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<T>()} 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() \fn int qMetaTypeId()
\relates QMetaType \relates QMetaType

View File

@ -426,6 +426,11 @@ public:
bool isValid() const; bool isValid() const;
bool isRegistered() const; bool isRegistered() const;
void registerType() const
{
// "register" is a reserved keyword
registerHelper();
}
#if QT_CORE_REMOVED_SINCE(6, 1) || defined(Q_QDOC) #if QT_CORE_REMOVED_SINCE(6, 1) || defined(Q_QDOC)
int id() const; int id() const;
#else #else
@ -434,12 +439,7 @@ public:
int id(int = 0) const int id(int = 0) const
{ {
// keep in sync with the version in removed_api.cpp // keep in sync with the version in removed_api.cpp
if (d_ptr) { return registerHelper();
if (int id = d_ptr->typeId.loadRelaxed())
return id;
return idHelper();
}
return 0;
} }
#endif #endif
constexpr qsizetype sizeOf() const; constexpr qsizetype sizeOf() const;
@ -722,7 +722,23 @@ public:
const QtPrivate::QMetaTypeInterface *iface() const { return d_ptr; } const QtPrivate::QMetaTypeInterface *iface() const { return d_ptr; }
private: private:
#if QT_CORE_REMOVED_SINCE(6, 5)
int idHelper() const; 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; friend class QVariant;
const QtPrivate::QMetaTypeInterface *d_ptr = nullptr; const QtPrivate::QMetaTypeInterface *d_ptr = nullptr;
}; };
@ -1285,6 +1301,9 @@ template <typename T>
inline constexpr int qMetaTypeId() inline constexpr int qMetaTypeId()
{ {
if constexpr (bool(QMetaTypeId2<T>::IsBuiltIn)) { if constexpr (bool(QMetaTypeId2<T>::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<T>::MetaType; return QMetaTypeId2<T>::MetaType;
} else { } else {
return QMetaType::fromType<T>().id(); return QMetaType::fromType<T>().id();
@ -1298,6 +1317,11 @@ inline constexpr int qRegisterMetaType()
return id; return id;
} }
inline int qRegisterMetaType(QMetaType meta)
{
return meta.registerHelper();
}
#ifndef QT_NO_QOBJECT #ifndef QT_NO_QOBJECT
template <typename T> template <typename T>
struct QMetaTypeIdQObject<T*, QMetaType::PointerToQObject> struct QMetaTypeIdQObject<T*, QMetaType::PointerToQObject>

View File

@ -1424,20 +1424,27 @@ void tst_QMetaType::typedefs()
QCOMPARE(QMetaType::type("WhityDouble"), ::qMetaTypeId<WhityDouble>()); QCOMPARE(QMetaType::type("WhityDouble"), ::qMetaTypeId<WhityDouble>());
} }
struct RegisterTypeType {};
void tst_QMetaType::registerType() void tst_QMetaType::registerType()
{ {
// Built-in // Built-in
QCOMPARE(qRegisterMetaType<QString>("QString"), int(QMetaType::QString)); QCOMPARE(qRegisterMetaType<QString>("QString"), int(QMetaType::QString));
QCOMPARE(qRegisterMetaType<QString>("QString"), int(QMetaType::QString)); QCOMPARE(qRegisterMetaType<QString>("QString"), int(QMetaType::QString));
qRegisterMetaType(QMetaType::fromType<QString>());
// Custom // Custom
int fooId = qRegisterMetaType<TestSpace::Foo>("TestSpace::Foo"); int fooId = qRegisterMetaType<TestSpace::Foo>("TestSpace::Foo");
QVERIFY(fooId >= int(QMetaType::User)); QVERIFY(fooId >= int(QMetaType::User));
QCOMPARE(qRegisterMetaType<TestSpace::Foo>("TestSpace::Foo"), fooId); QCOMPARE(qRegisterMetaType<TestSpace::Foo>("TestSpace::Foo"), fooId);
qRegisterMetaType(QMetaType::fromType<TestSpace::Foo>());
int movableId = qRegisterMetaType<CustomMovable>("CustomMovable"); int movableId = qRegisterMetaType<CustomMovable>("CustomMovable");
QVERIFY(movableId >= int(QMetaType::User)); QVERIFY(movableId >= int(QMetaType::User));
QCOMPARE(qRegisterMetaType<CustomMovable>("CustomMovable"), movableId); QCOMPARE(qRegisterMetaType<CustomMovable>("CustomMovable"), movableId);
qRegisterMetaType(QMetaType::fromType<CustomMovable>());
// Aliases are deprecated
// Alias to built-in // Alias to built-in
typedef QString MyString; typedef QString MyString;
@ -1460,6 +1467,23 @@ void tst_QMetaType::registerType()
QCOMPARE(qRegisterMetaType<MyFoo>("MyFoo"), fooId); QCOMPARE(qRegisterMetaType<MyFoo>("MyFoo"), fooId);
QCOMPARE(QMetaType::type("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<RegisterTypeType>();
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 { }; class IsRegisteredDummyType { };