[Development] Metatype system in Qt6
Olivier Goffart
olivier at woboq.com
Thu Dec 5 17:55:35 CET 2019
Hi,
Reviving an old thread as I am currently working on QMetaType.
QMetaType is a low level API of Qt, normally a developer shouldn't use it much
in its application, and the goal for Qt6 is that there is even less to know
about it. In short, the goal was to remove the use for Q_DECLARE_METATYPE,
qRegisterMetaType, and other particularities, while also making it faster to
use properties and method from QML.
The idea is that the moc generated code would put already the QMetaType in its
data structure (by calling the equivalent of qMetaTypeId())
But that means that the type need to be fully defined in the moc generated file.
let's imagine:
class MyType; // forward declared
class MyObject : public QObject { Q_OBJECT
Q_PROPERTY(MyType *prop ...)
signals:
void mySignal(MyType *);
slots:
void mySlot(MyType *);
// ...
};
That will not be working anymore if the MyType is only worward declared.
The user will have to do one of these:
1. #include "MyType.h" in the header
2. Q_DECLARE_OPAQUE_POINTER(MyType *)
3. Q_MOC_INCLUDE("MyType.h") : that's not yet implemented but would make moc
include the file in the generated file.
That's a source incompatible change, and the question is if we really want it.
For the property types, i'd say yes: the types need to be registered for the
properties to be useful anyway.
For the types in the signals an and slot, maybe not. If you do not use
QueuedConnection, you do not need to register the types. (well, actually, we
still need to compare their name for the string based connection syntax)
however, to be called from QML, then the type need to be register. I'm tempted
to say that they should be registered. as well.
Now another question is what exactly do we want to automatically register.
Of course, we will register the way to construct, copy, and destruct the types.
Now, should we also register automatically the other things that we can do:
- operator==
- QDataStream operators
- QDebug operator
Ideally we also want this to be automatic, but i've run into an issue.
I naively tought it would be simple enough to just use SFINAE to detect if the
type has the operator==, with something like that.
template<typename T, typename=bool> struct HasEqualityOperator : false_type {};
template<typename T> struct HasEqualityOperator<T,
decltype(declval<T>() == declval<T>())> : true_type {};
But the problem is that this will return true_type if the operator exists, not
if it compile.
Say I have a `struct CustomType { int x; };` that cannot be compared.
This can registered as a QMetaType and the operator== will not be registered.
But now what about `QVector<CustomType>`? If one tries to register that as an
QMetaType, it will register the operator== because QVector has one. The
problem is that when trying to actually compile it, the compilation will fail :-(
So that means QVector<CustomType> cannot be registered without causing a
compilation error, or one need to explicitly delete the
`operator==(QVector<CustomType>, QVector<CustomType>)`
Same problem with the stream operators and debug operators.
Also registering them automatically implies that the operator need to be
visible so qdatastream and qdebug need to be included everywhere.
So the question is if this breaking change is worth it?
Do we want to automatically register the operators?
Do we want to automatically register the types used in signal/slots, or just
the properties?
--
Olivier
More information about the Development
mailing list