[Development] Metatype system in Qt6

Olivier Goffart olivier at woboq.com
Tue Oct 30 13:29:35 CET 2018


Hi,


On 10/30/18 12:38 PM, Jedrzej Nowacki wrote:
[...]
> Extensions in that respect do not change anything, because it is about error
> handling. How to inform a user that a type is not qdebug stream-able 

Not really important. I just want to do
  qDebug() << my_variant;
and have some useful information if possible.  This is just for printf-style 
debugging. I don't want to add a line before such as:
  qRegisterDebugOperator<MyType>(); // maybe my_variant contains MyType so i 
need to register it.

I expect that if something goes into the QVariant, it has everything.

> or can not be converted to QString. 

{QMetaType/QVariant}::canConvert,  but let's talk about conversion later.

> In the current and the data driven approach you
> would need to check if the right function pointer is null or not and on top of
> this the error handling still would need to happen as the call itself could
> fail. With the proposed solution you have only one point in which you handle
> potential errors, just after the metatype call.

I don't understand why, internally, handling a null function pointer (or check 
the return code of a metacall function) is a problem. Also i do not see how the 
proposed extension system solve this. Please elaborate.

> So let's return to QVariant concern that you expressed. I believe that if you
> plan to use a feature you need to ensure that you can. So I would propose
> QVariant constructor to look more or less like qVariantFromValue, it would do
> the "registration" it needs. It is in sync with QObject registering own
> properties types.

Right, exactly!
So QVariant would call qMetaTypeId<T>()  which would take care that the type is 
registered, with all features.

[...]
>> So what are the operation we want on a QMetaType:
>>
>> - for QVariant, and the queued connections: copy / destruction.  Ideally in
>> place. So we also need the size/alignement to be able to allocate the memory
>>
>> - for qDebug, we need the QDebug operator <<
>>
>> - for QDataStream, we need the operator << and >>, and an unique identifier
>> that stay the same. Currently, this is the type id for builtin types, and
>> the name for custom type. I suggest we use the name, if we want to keep
>> compatibility with old steam, we will need to keep a mapping from old type
>> id, to new name.
> I'm pretty sure that you are aware that the list is far from being complete,

Yes. I might have forgot a couple of things. Some flags, registering the 
QMetaObject, conversion function, indeed.

Speaking of conversion. I think you agree that this huge conversion martix to 
try to convert from and to anything is a bad design we should review.
I've heard you in the past claiming that you want to remove any conversion.
I think we probably should just enable conversion to/from some basic primitive 
type: string and numbers.
I'm not sure this is usefull to let QVariant have the ability to convert from a 
QImage to a QPixmap.

> so I will not neat pick on this, but how you make sure that every future use
> case would satisfied? 

Say we decide, in Qt 6.4, to add the std::iostream operators while this was no 
planed before: we just add them. I don't see the problem.
We need to be carefull about binary compatibility and types registered in older 
application, but that is not a big issue.

We can also add whatever in Qt 5.x lifetime. We added some data to the 
QMetatype within Qt 5.x lifetime. This is not a problem.

> Hiding functionality behind a function and extensible
> data struct allow _us_ to extend it if _we_ believe that costs are worth the
> gain, but it doesn't solve problem for 3rdparty code. We do see the problem
> even in our modules that needs to keep a sibling mapping from typeid to some
> functions, in particular dbus and qml(?).

Here you make a good case.
If I understand correctly, you don't want dbus and qml to have themself a global:
   QHash<QMetaTypeId, QmlTypeExtenstion> typeRegistery; // guarded by typeMutex

In this respect, there is indeed value for extensions.
As long as the basic stuff are not extensions it is fine.
(I would hope that neither Qml nor dbus need extensions though)

[...]
>> In Qt5, we also need the name for the string-based connection syntax.
>> However, I believe we can do that without relying on the name
>> "as-seen-by-moc", if the moc generated code always register the types.
>> (this is already almost the case)
> Why almost? Is it that because of forward declared types?

Because it only work for type that moc can determine to be automatically 
registrable. Ideally, all types should be registerable.

But moc has a conservative aproch here, doing it to eager can have source 
incompatibilities issue

moc-ng [https://github.com/woboq/moc-ng] already registers all types that can 
be registered. And I guess moc should do the same.

verdigris [https://github.com/woboq/verdigris] also register all types.

We have the problem with pointer to forward declared class. That can cause ODR 
violation and is indeed a problem.

>> You will need that anyway, because relying on a static variable in a
>> function template does not work on MSVC as far as i know. (Last time i
>> tried was a long time ago, and they had different address and value in
>> different libraries) That's why we will probably need to initialize it with
>> a name lookup.
> Ugh, that is an ugly problem. This https://github.com/nierob/qmetatype/blob/
> master/metatype_impl.h#L80 one really requires the unification of variables.
> You are right we can workaround it with a global registry and name lookup,
> but... oh.

I know, ...

Previous work:
https://bugreports.qt.io/browse/QTBUG-19137
https://codereview.qt-project.org/2106
Yeah.. that's old. Too bad that did not go in Qt5



-- 
Olivier

Woboq - Qt services and support - https://woboq.com - https://code.woboq.org



More information about the Development mailing list