[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