Discussion:
[Interest] how can I call qRegisterMetaType?
Hamish Moffatt
2016-05-16 02:33:58 UTC
Permalink
I've got an enum in a class which I need to register with
qRegisterMetaType() (in order to use it in object properties). I also
need to register some converters with QMetaType::registerConverter().

class MyQuestion : public QObject
{
Q_OBJECT

public:
enum QuestionType { TYPE_A, TYPE_B, TYPE_C, TYPE_D };
Q_ENUM(QuestionType)

};

Is there a way to keep the call to qRegisterMetaType to the class so
that the code using my class doesn't need to worry about it? For example
I tried using a class member and method, as in

bool MyQuestion::registered = registerTypes();
bool MyQuestion::registerTypes()
{
qRegisterMetaType<MyQuestion::QuestionType>("QuestionType");
}


However this blows up in the qRegisterMetaType call which suggests that
the initialisation happened too early. It looks like it happened before
the Q_ENUM worked its magic. Here's the stack trace (MSVC 2013):

Qt5Cored.dll!objectClassName(const QMetaObject * m=0x002fa404)
Line 301 C++
Qt5Cored.dll!QMetaObject::className() Line 311 C++
json.exe!QMetaTypeIdQObject<enum
MyQuestion::QuestionType,16>::qt_metatype_id() Line 1806 C++
json.exe!QMetaTypeId2<enum
MyQuestion::QuestionType>::qt_metatype_id() Line 1584 C++
json.exe!QtPrivate::QMetaTypeIdHelper<enum
MyQuestion::QuestionType,1>::qt_metatype_id() Line 1597 C++
json.exe!qRegisterNormalizedMetaType<enum
MyQuestion::QuestionType>(const QByteArray & normalizedTypeName={...},
MyQuestion::QuestionType * dummy=0x00000000,
QtPrivate::MetaTypeDefinedHelper<enum
MyQuestion::QuestionType,1>::DefinedType defined=Defined) Line 1666 C++
json.exe!qRegisterMetaType<enum MyQuestion::QuestionType>(const
char * typeName=0x002f6370, MyQuestion::QuestionType * dummy=0x00000000,
QtPrivate::MetaTypeDefinedHelper<enum
MyQuestion::QuestionType,1>::DefinedType defined=Defined) Line 1705 C++
json.exe!MyQuestion::registerTypes() Line 22 C++
json.exe!`dynamic initializer for 'MyQuestion::registered''() Line
14 C++
msvcr120d.dll!_initterm(void (void) * * pfbegin=0x002f5240, void
(void) * * pfend=0x002f5370) Line 955 C
json.exe!__tmainCRTStartup() Line 550 C
json.exe!WinMainCRTStartup() Line 466 C
kernel32.dll!@***@12() Unknown
ntdll.dll!__RtlUserThreadStart() Unknown
ntdll.dll!***@8() Unknown


Should I just do it from the constructor? Would I need to worry about
thread safety then, if my class was created in a bunch of threads at the
same time while this is going on?

thanks,
Hamish
Nye
2016-05-16 10:28:09 UTC
Permalink
Post by Hamish Moffatt
Should I just do it from the constructor?
That would be what I usually do. Still there's overhead of repeatedly
registering the same type, so if you have a better place on hand (a root
object/entry point of your application/library) that'd be better.
Post by Hamish Moffatt
Would I need to worry about thread safety then, if my class was created in
a bunch of threads at the same time while this is going on?
qRegisterMetaType is thread-safe.

Kind regards,
Konstantin.
Thiago Macieira
2016-05-16 15:27:30 UTC
Permalink
Post by Nye
Post by Hamish Moffatt
Should I just do it from the constructor?
That would be what I usually do. Still there's overhead of repeatedly
registering the same type, so if you have a better place on hand (a root
object/entry point of your application/library) that'd be better.
The overhead is almost null because the inline parts of qRegisterMetaType
function check whether it's already registered before calling the non-inline
parts.

PS: DO NOT use the qRegisterMetaType overload with 1 argument. Use the one
with zero.
Post by Nye
Post by Hamish Moffatt
Would I need to worry about thread safety then, if my class was created in
a bunch of threads at the same time while this is going on?
qRegisterMetaType is thread-safe.
Kind regards,
Konstantin.
--
Thiago Macieira - thiago.macieira (AT) intel.com
Software Architect - Intel Open Source Technology Center
Hamish Moffatt
2016-05-16 23:47:18 UTC
Permalink
Post by Thiago Macieira
Post by Nye
Post by Hamish Moffatt
Should I just do it from the constructor?
That would be what I usually do. Still there's overhead of repeatedly
registering the same type, so if you have a better place on hand (a root
object/entry point of your application/library) that'd be better.
The overhead is almost null because the inline parts of qRegisterMetaType
function check whether it's already registered before calling the non-inline
parts.
How about QMetaType::registerConverter? The documentation says all
QMetaType functions are thread-safe. Is it also fast enough to call
repeatedly?
Post by Thiago Macieira
PS: DO NOT use the qRegisterMetaType overload with 1 argument. Use the one
with zero.
What's the difference? When is the one with the char* typeName parameter
needed?

I have a Q_PROPERTY of type QList<MyClass::MyEnum>, and I find that if I
call

qRegisterMetaType<QList<MyEnum>>();

(ie without a typeName parameter), then I am unable to read the property
through QMetaProperty::read(), and a message is printed: "Unable to
handle unregistered data type 'QList<MyEnum>' for property
'MyClass::propertyName'". Adding the name parameter solves it. However a
QList<int> property works without giving a name.

And do I need to call
Q_DECLARE_METATYPE(QList<MyQuestion::QuestionType>) or not? It doesn't
seem to be necessary in my test cases.



Hamish
Thiago Macieira
2016-05-17 01:04:34 UTC
Permalink
Post by Hamish Moffatt
Post by Thiago Macieira
The overhead is almost null because the inline parts of qRegisterMetaType
function check whether it's already registered before calling the
non-inline parts.
How about QMetaType::registerConverter? The documentation says all
QMetaType functions are thread-safe. Is it also fast enough to call
repeatedly?
It's fast, but that one prints a warning:

if (!customTypesConversionRegistry()->insertIfNotContains(qMakePair(from,
to), f)) {
qWarning("Type conversion already registered from type %s to type %s",
QMetaType::typeName(from), QMetaType::typeName(to));
return false;
}
return true;
Post by Hamish Moffatt
Post by Thiago Macieira
PS: DO NOT use the qRegisterMetaType overload with 1 argument. Use the one
with zero.
What's the difference? When is the one with the char* typeName parameter
needed?
The name comes from your Q_DECLARE_METATYPE macro, so you don't need to pass
it. Therefore, using the version with the name means you're typing more than
you have to.

And you're bypassing the check to see if the type has already been registered.
Post by Hamish Moffatt
I have a Q_PROPERTY of type QList<MyClass::MyEnum>, and I find that if I
call
qRegisterMetaType<QList<MyEnum>>();
(ie without a typeName parameter), then I am unable to read the property
through QMetaProperty::read(), and a message is printed: "Unable to
handle unregistered data type 'QList<MyEnum>' for property
'MyClass::propertyName'". Adding the name parameter solves it. However a
QList<int> property works without giving a name.
And do I need to call
Q_DECLARE_METATYPE(QList<MyQuestion::QuestionType>) or not? It doesn't
seem to be necessary in my test cases.
Note that it's a macro declaring a template specialisation, so it's not a
call.

Yes, you must declare your metatypes before registering them. Remember the
part about there being a variable that avoids the multiple registrations?
That's created by this macro.
--
Thiago Macieira - thiago.macieira (AT) intel.com
Software Architect - Intel Open Source Technology Center
Hamish Moffatt
2016-05-17 01:41:59 UTC
Permalink
Post by Thiago Macieira
Post by Hamish Moffatt
And do I need to call
Q_DECLARE_METATYPE(QList<MyQuestion::QuestionType>) or not? It doesn't
seem to be necessary in my test cases.
Note that it's a macro declaring a template specialisation, so it's not a
call.
Yes, you must declare your metatypes before registering them. Remember the
part about there being a variable that avoids the multiple registrations?
That's created by this macro.
It seems that for a metatype that is a QList<MyClass::MyEnum> I must
call qRegisterMetaType with the name specified, else reading properties
complains that the metatype isn't registered. What worries me is that
the type name must be specified without the class, as in

qRegisterMetaType<QList<MyClass::MyEnum>>("QList<MyEnum>");

If I have another enum MyOtherClass::MyEnum am I going to have collisions?

Sample code:


#include <QObject>
#include <QList>
#include <QDebug>

class MyClass : public QObject
{
Q_OBJECT
Q_PROPERTY(MyEnum myEnum MEMBER myEnum)
Q_PROPERTY(QList<MyEnum> myEnumList MEMBER myEnumList)

public:
enum MyEnum { EA, EB, EC };
Q_ENUM(MyEnum)

MyEnum myEnum;
QList<MyEnum> myEnumList;

MyClass()
: QObject(nullptr)
, myEnum(EB)
, myEnumList({ EB, EB, EC })
{}

};

Q_DECLARE_METATYPE(QList<MyClass::MyEnum>)

int main(int argc, char** argv)
{
qRegisterMetaType<MyClass::MyEnum>();
qRegisterMetaType<QList<MyClass::MyEnum>>();

MyClass c;
QVariant v = c.property("myEnum");
qDebug() << v;

v = c.property("myEnumList"); // will print an error
qDebug() << v;

qRegisterMetaType<QList<MyClass::MyEnum>>("QList<MyClass::MyEnum>");

v = c.property("myEnumList"); // will print an error
qDebug() << v;

qRegisterMetaType<QList<MyClass::MyEnum>>("QList<MyEnum>");

v = c.property("myEnumList"); // will succeed
qDebug() << v;

return 0;
}

#include "meta.moc"



Hamish
Thiago Macieira
2016-05-17 03:42:45 UTC
Permalink
Post by Hamish Moffatt
It seems that for a metatype that is a QList<MyClass::MyEnum> I must
call qRegisterMetaType with the name specified, else reading properties
complains that the metatype isn't registered. What worries me is that
the type name must be specified without the class, as in
qRegisterMetaType<QList<MyClass::MyEnum>>("QList<MyEnum>");
If I have another enum MyOtherClass::MyEnum am I going to have collisions?
Yes.

Always register metatypes with their full names, always use the full name in
signals and slots. That's what happens when string comparisons are used.
--
Thiago Macieira - thiago.macieira (AT) intel.com
Software Architect - Intel Open Source Technology Center
Hamish Moffatt
2016-05-17 07:04:57 UTC
Permalink
Post by Thiago Macieira
Post by Hamish Moffatt
It seems that for a metatype that is a QList<MyClass::MyEnum> I must
call qRegisterMetaType with the name specified, else reading properties
complains that the metatype isn't registered. What worries me is that
the type name must be specified without the class, as in
qRegisterMetaType<QList<MyClass::MyEnum>>("QList<MyEnum>");
If I have another enum MyOtherClass::MyEnum am I going to have collisions?
Yes.
Always register metatypes with their full names, always use the full name in
signals and slots. That's what happens when string comparisons are used.
But, as per my example code,

qRegisterMetaType<QList<MyClass::MyEnum>>("QList<MyClass::MyEnum>");


doesn't work; QMetaProperty::read prints

QMetaProperty::read: Unable to handle unregistered datatype 'QList<MyEnum>' for property 'MyClass::myEnumList'

ie it can't find the type.


Hamish
Thiago Macieira
2016-05-17 08:07:35 UTC
Permalink
Post by Hamish Moffatt
QMetaProperty::read: Unable to handle unregistered datatype 'QList<MyEnum>'
for property 'MyClass::myEnumList'
ie it can't find the type.
Fix the point where that read() is called.
--
Thiago Macieira - thiago.macieira (AT) intel.com
Software Architect - Intel Open Source Technology Center
Hamish Moffatt
2016-05-17 10:04:32 UTC
Permalink
Post by Thiago Macieira
Post by Hamish Moffatt
QMetaProperty::read: Unable to handle unregistered datatype 'QList<MyEnum>'
for property 'MyClass::myEnumList'
ie it can't find the type.
Fix the point where that read() is called.
That's just QObject::property() ....


Hamish
Thiago Macieira
2016-05-17 16:19:18 UTC
Permalink
Post by Hamish Moffatt
Post by Thiago Macieira
Post by Hamish Moffatt
QMetaProperty::read: Unable to handle unregistered datatype
'QList<MyEnum>'
for property 'MyClass::myEnumList'
ie it can't find the type.
Fix the point where that read() is called.
That's just QObject::property() ....
Indeed, but where did it get the name? Probably from the Q_PROPERTY
declaration. So add the full qualification there.
--
Thiago Macieira - thiago.macieira (AT) intel.com
Software Architect - Intel Open Source Technology Center
Alexey Rusakov
2016-05-17 04:47:55 UTC
Permalink
And do I need to call Q_DECLARE_METATYPE(QList<MyQuestion::QuestionType>) or
not? It doesn't seem to be necessary in my test cases
I don't think so. The documentation for Q_DECLARE_METATYPE says that
if Type is registered then QList<Type> is automatically recognized by
the metatype system as well. And as far as I understand the
documentation, this applies to types declared as Q_ENUM as well.
--
Alexey Rusakov
Loading...