QMetaType knows your types
QMetaType is Qt's way to have run-time dynamic information about your types. It enables things such as QVariant wrapping of custom types, copy of queued connection arguments, and more.
If you ever wondered what does Q_DECLARE_META_TYPE
or qRegisterMetaType
do and when to use (or not to use) them, read on.
This article will describe what you need to know about QMetaType: What is its purpose; How to use it;
And how it works.
Why does Qt need runtime dynamic type information?
Let's start with a bit of history. QMetaType
was introduced in Qt 4.0. It was created in order to have the possibility
to have asynchronous signals and slots (Qt::QueuedConnection
).
For queued slots to work, we have to copy the arguments and store them in an event that will be
processed later. We also need to delete those copies when we are finished invoking the slot.
(Note: This is not needed when using Qt::DirectConnection
: pointers to arguments directly on the stack are used.)
The code dispatching signals in
QMetaObject::activate
has an array of pointers to arguments void*
.
(For more info read how signals and slots work).
But, at the time, all Qt knows about the argument types are their name as a string, extracted by moc.
QMetaType
provides a way from the string (e.g. "QPoint"
) to get a to copy or destroy the object.
Qt would then use void *QMetaType::create(int type, void *copy)
and
QMetaType::destroy(int type, void *data)
to copy and destroy the arguments, where the int type
is obtained using QMetaType::type(const char *typeName)
using the type name of
the argument, as provided by moc. QMetaType also provides a way for the developer to register any kind
of type in the meta type database.
Another use case for QMetaType is QVariant. The QVariant from Qt 3.x only supported built-in Qt types because a contained arbitrary type would also need to be copied or destroyed together with the wrapping QVariant. But with the help of QMetaType, QVariant was able to contain any registered type since QVariant can now copy and destroy contained instances of objects.
What information does QMetaType keep?
Since Qt 4.0 a lot has changed. We now have QtScript and QML which are making intensive use of the dynamic type integration. And we had to optimize a lot.
Here is the list of information kept for each type in the meta-type system:
- The Type Name as registered. There is a name index for fast lookup of the meta type id. Since Qt 4.7, it is even possible to register the same type with different names (useful for typedefs).
- (Copy) Constructor and Destructor (in-place or not).
- Size to know how much space to allocate for a stack or inline member construction.
- Flags specifying the same information as QTypeInfo (see bellow) or the type of conversion.
-
Custom conversion functions, set by
QMetaType::registerConverter
. - QMetaObject, containing the meta QObject data associated with a type if it exists.
- …
QTypeInfo
QTypeInfo
is a trait class orthogonal to QMetaType, it allows the developer to
manually specify (using the Q_DECLARE_TYPEINFO
) that a type is movable (using memmove) or if its
constructor/destructor need to be run.
This is mainly used for optimization in containers like QVector.
For example, the implicitly shared classes may be moved with memmove. While a normal copy should first increase the reference count with the copy constructor and then decrease it in the destructor.
C++11 introduces move constructors and standard type traits to solve this problem, but since QTypeInfo was designed way before C++11 and Qt still has to work with older compiler, we have to do without.
How does it work?
For historical reason, there is a big difference between built-in types and custom types. For built-ins types in QtCore, each meta-type function is basically a switch that has special code for each type. In Qt 5.0 this was re-factored to use templates a lot. (See QMetaTypeSwitcher.) But what is going to interest us in this article is how it works for custom registered types.
There is simply a QVector<QCustomTypeInfo>
that holds all the information and a bunch of function pointer.
The Q_DECLARE_METATYPE
macro.
That macro specializes the template class QMetaTypeId
for the specific type.
(In fact, it actually specializes the class QMetaTypeId2
and most of the code uses QMetaTypeId2.
I don't know the exact reason behind QMetaTypeId2. Maybe so that Qt can add more built-in types without
breaking code that used Q_DECLARE_METATYPE
before.)
QMetaTypeId
is used to determine the meta-type id at compile time for a type.
QMetaTypeId::qt_metatype_id
is the function called by the qMetaType<T>()
.
On the first call of this function, it will call to some internal function within QMetaType to register and allocate
a meta-type id for this type, using the name specified in the macro. It will then store that id in a static variable.
Apart from the name, all other information is automatically inferred by the compiler using templates.
qRegisterMetaType
Type registered using Q_DECLARE_METATYPE
are going to be actually registered (and be assigned an id) on the first use of qMetaTypeId()
.
That's the case when a type is wrapped in a QVariant for example.
But this is not yet registered when connecting signals and slots. In that case you need to force the first use using
qRegisterMetaType
Automatic registration.
Developers often forget to register their meta-type until they see the compilation error or run-time error
telling them to do so. But wouldn't it be nice if it would not be necessary?
The only reason Q_DECLARE_METATYPE
is necessary is to get the name. But there are cases where we can know the
name at run-time without that macro. For example, QList<T>
if T is already registered, we can query
the meta-type system and construct the name using "QList<" + QMetaType::name(qMetaTypeId<T>()) + ">"
We do that for a bunch of templated classes, for example: QList, QVector, QSharedPointer, QPointer, QMap, QHash, ...
We can also determine the name of a pointer to a QObject subclass thanks to the information provided by moc:
T::staticMetaObject.className() + "*"
And from Qt 5.5, we will also automatically declare the Q_GADGET
and Q_ENUM
.
That was it for Q_DECLARE_METATYPE
, but you would still need to call qRegisterMetaType
to use these type
in a Q_PROPERTY
or as a parameter in a signal/slot queued connection.
Since Qt 5.x however, the code generated by moc will call qRegisterMetaType
for you if moc can determine that the type may be registered as a meta-type.
Research
Before Qt 5.0, I was trying to investigate if we would not be able to get rid of Q_DECLARE_METATYPE
for cases
in which we do not need the name. This worked somehow like this:
template<typename T> QMetaTypeId { static int qt_metatype_id() { static int typeId = QMetaType::registerMetaType(/*...*/); return typeId; } };
According to the C++ standard, there shall be exactly one instance of
the variable QMetaTypeId::qt_metatype_id()::typeId
for each type. But in practice some
compilers or linkers do not obey this rule. In particular, on Windows, there would be one instance
per library even when using the proper export macro.
We would therefore always need a name identifier which we don't have.
(And we don't want to rely on RTTI).
Therefore we only register the type for which we can know the name in Qt 5.
Woboq is a software company that specializes in development and consulting around Qt and C++. Hire us!
If you like this blog and want to read similar articles, consider subscribing via our RSS feed (Via Google Feedburner, Privacy Policy), by e-mail (Via Google Feedburner, Privacy Policy) or follow us on twitter or add us on G+.
Article posted by Olivier Goffart on 22 April 2015
Click to subscribe via RSS or e-mail on Google Feedburner. (external service).
Click for the privacy policy of Google Feedburner.
Google Analytics Tracking Opt-Out
Loading comments embeds an external widget from disqus.com.
Check disqus privacy policy for more information.