Qobject_cast triggers an assertion on QML components [SOLVED]
-
I'm using this code to iterate through the children of my custom object (C++, derived from QObject) and check for a certain type:
@
foreach (QObject *o, children()) {
Foo f = qobject_cast<Foo>(o);
if (f) {
// do foo stuff with f
}
}
@This works great as long as there are only C++ objects in children(). However, when I make a component that extends a "Foo", like this:
@import FooLib 1.0
Foo {
...
}
@qobject_cast<Foo *> triggers an assertion failure:
@ASSERT: "!dp->isFunction()" in file qml/qqmlpropertycache.cpp, line 1509@
Here's a stack trace:
@148 raise() 0x0000003562a32885
147 abort() 0x0000003562a34065
146 qt_message_fatal() qlogging.cpp:806 0x00007ffff6758da9
145 QMessageLogger::fatal() qlogging.cpp:355 0x00007ffff6756ffa
144 qt_assert() qglobal.cpp:1951 0x00007ffff675233c
143 QQmlPropertyCache::toMetaObjectBuilder() qqmlpropertycache.cpp:1509 0x00007ffff7c21db5
142 QQmlPropertyCache::createMetaObject() qqmlpropertycache.cpp:528 0x00007ffff7c1dc69
141 QQmlVMEMetaObject::toDynamicMetaObject() qqmlvmemetaobject.cpp:539 0x00007ffff7b66944
140 QObjectData::dynamicMetaObject() qobject.cpp:189 0x00007ffff69732f4
139 ShotgunEntity::metaObject() moc_shotgunentity.cpp:100 0x00007fffec20e16b
138 QMetaObject::cast() qmetaobject.cpp:338 0x00007ffff6941eda
137 qobject_cast<Property*>() qobject.h:451 0x00007fffec1d03b6
136 NodeBase::inputs() nodebase.cpp:364 0x00007fffec1cf758 @Am I doing something wrong?
thanks,
-Mark -
This is because your QML type is not "Foo". It's some internal type like QMLTYPE_123. You can check
@
Foo {
id: object
Component.onCompleted: console.log(object)
}
@My guess is that in QML, inheritance works differently, for example through composition.
-
This seems to be a bug in QML. The meta data from QML Components is stored in a structure called QQmlPropertyCache. The QMetaObject is then created on demand (In this case the qobject_cast).
The creation of the QMetaObject seems to fail/assert in your case.
-
bq. My guess is that in QML, inheritance works differently, for example through composition.
Good point, I think they're all internally instances of QQmlComponent or some such. I'll have to figure out a different way to check if a QObject conforms to a particular interface.
bq. The QMetaObject is then created on demand (In this case the qobject_cast).
Well, it tries to create the metaobject on demand, anyway. I'm not sure why, but QML component objects do not have an associated QMetaObject. I discovered this when trying to see how hard it would be to add support for native QML attached properties objects (answer: hard enough that I gave up).
I'll whip up a small test case when I have a chance and file a bug report.
So bug aside, anyone know how to check if a QML instance conforms to an interface?
edit: I tried guarding the qobject_cast with
@if (o->inherits("Foo"))@The qobject_cast still triggers an assertion for QML instances "derived" from Foo.
-
Solved!
I figured it out after being unable to reproduce it in a simplified test case.
The base class of Foo has a default property called "data", patterned after QQmlItem. I had forgotten this and added an invokable method, also called "data", to Foo. In concrete instances of Foo, this wasn't a problem because they had different method signatures, but as soon as I started using QML instances, the invokable clobbered the default property.