How to manage QObject instance which is shared among lots of C++/QML places
-
Suppose I have an QObject-derived instance.
If I have to share this instance in C++, I'll use QSharedPointer or std::shared_ptr.
If I have to share this instance in QML, I'll set the ownership to JavascriptOwnership and let the QML engine manage it.However, now I have to share this instance in both of C++ and QML.
I cannot set JavascriptOwnership because this instance should be alive while used in C++.
I cannot use QSharedPointer because once I expose raw pointer encapsualted in QSharedPointer to QML, reference-counting makes no more sense.How should I manage such instance?
@xylosper said in How to manage QObject instance which is shared among lots of C++/QML places:
If I have to share this instance in C++, I'll use QSharedPointer or std::shared_ptr.
You shouldn't do that even from C++. A shared pointer holds an instance to an object that manages its own lifetime and
QObject
s don't! Their lifetime is either managed by their parent or manually by you. You should either useQScopedPointer
orQPointer
depending on the situation.As a side note you can't use implicit sharing with
QObject
s as @raven-worx suggested. That'd be no different than holding it inQSharedPointer
If you need it to persist throughout I'd parent it to the application object and register it as a global object for QML before the engine starts up. However you should elaborate on the lifetime of said object, when should it be created and when should it be destroyed?
-
@xylosper
you could use the same approach like Qt's implicit sharing.
The wrapper class can be created multiple times. Each wrapper class holds an shared pointer to the actual data. (make sure you implement the copy constructor and assignment operator).If I write a wrapper class. doesn't it mean that I have to write duplicated slots/signals?
You shouldn't do that even from C++. A shared pointer holds an instance to an object that manages its own lifetime and QObjects don't! Their lifetime is either managed by their parent or manually by you. You should either use QScopedPointer or QPointer depending on the situation.
Why not? It's just a pointer to an object. All my instances does not have a parent.
It's derived from QObject only for signal.
Even if it has a parent, Qt will remove the child information automatically in child list of parent when the instance deleted at outside of parent's dtor. Or, may I miss something here..?If you need it to persist
Nope, my instance is not permanent. It can be created in both of C++ and QML and also can be deleted in both of C++ and QML.
Also, it is not a unique one. It cannot be a singletone. -
If I write a wrapper class. doesn't it mean that I have to write duplicated slots/signals?
You shouldn't do that even from C++. A shared pointer holds an instance to an object that manages its own lifetime and QObjects don't! Their lifetime is either managed by their parent or manually by you. You should either use QScopedPointer or QPointer depending on the situation.
Why not? It's just a pointer to an object. All my instances does not have a parent.
It's derived from QObject only for signal.
Even if it has a parent, Qt will remove the child information automatically in child list of parent when the instance deleted at outside of parent's dtor. Or, may I miss something here..?If you need it to persist
Nope, my instance is not permanent. It can be created in both of C++ and QML and also can be deleted in both of C++ and QML.
Also, it is not a unique one. It cannot be a singletone.@xylosper said in How to manage QObject instance which is shared among lots of C++/QML places:
If I write a wrapper class. doesn't it mean that I have to write duplicated slots/signals?
Do the connection (signal forwarding) in the copy constructor and assignment operator. Since you anyway want to use it in QML you can use the automatic property binding (declare a Q_PROPERTY in the wrapper class).
@kshegunov
i also don't see where the problem is? Just by using a QObject instance doesnt mean that you have to put it an parent-child-relationship. -
If I write a wrapper class. doesn't it mean that I have to write duplicated slots/signals?
You shouldn't do that even from C++. A shared pointer holds an instance to an object that manages its own lifetime and QObjects don't! Their lifetime is either managed by their parent or manually by you. You should either use QScopedPointer or QPointer depending on the situation.
Why not? It's just a pointer to an object. All my instances does not have a parent.
It's derived from QObject only for signal.
Even if it has a parent, Qt will remove the child information automatically in child list of parent when the instance deleted at outside of parent's dtor. Or, may I miss something here..?If you need it to persist
Nope, my instance is not permanent. It can be created in both of C++ and QML and also can be deleted in both of C++ and QML.
Also, it is not a unique one. It cannot be a singletone.@xylosper, @raven-worx
https://forum.qt.io/topic/74147/share-qstring-between-threads
It includes links to the mailing list with more information. Basically the problem is you can't guarantee the time of destruction of the object (implying multithreading here), which in turn leads to the possibility of having a queued event that is handled by a deletedQObject
. The result is a cryptic segfault in the event loop processing. -
@xylosper, @raven-worx
https://forum.qt.io/topic/74147/share-qstring-between-threads
It includes links to the mailing list with more information. Basically the problem is you can't guarantee the time of destruction of the object (implying multithreading here), which in turn leads to the possibility of having a queued event that is handled by a deletedQObject
. The result is a cryptic segfault in the event loop processing.@kshegunov
yes, but where is the threading involved here? -
@kshegunov
yes, but where is the threading involved here?@raven-worx said in How to manage QObject instance which is shared among lots of C++/QML places:
@kshegunov
yes, but where is the threading involved here?Doesn't QML employ threads internally? I might be wrong, but I thought it did.
-
@raven-worx said in How to manage QObject instance which is shared among lots of C++/QML places:
@kshegunov
yes, but where is the threading involved here?Doesn't QML employ threads internally? I might be wrong, but I thought it did.
@kshegunov
AFAIK it runs on the GUI thread -
@kshegunov
AFAIK it runs on the GUI thread@raven-worx
Fair enough. If that's the case, my comment can be disregarded. -
Suppose I have an QObject-derived instance.
If I have to share this instance in C++, I'll use QSharedPointer or std::shared_ptr.
If I have to share this instance in QML, I'll set the ownership to JavascriptOwnership and let the QML engine manage it.However, now I have to share this instance in both of C++ and QML.
I cannot set JavascriptOwnership because this instance should be alive while used in C++.
I cannot use QSharedPointer because once I expose raw pointer encapsualted in QSharedPointer to QML, reference-counting makes no more sense.How should I manage such instance?
Doesn't QML employ threads internally? I might be wrong, but I thought it did.
Yes, generally, QML runs on both of main GUI thread and scene graph rendering thread.
But the rendering thread won't appear unless you're writing some custom rendering items.
Things unrelated with rendering, all QML codes run on GUI thread, and my object lives in GUI thread only.
FYI, please see Threaded Render Loop ("threaded") section from here http://doc.qt.io/qt-5/qtquick-visualcanvas-scenegraph.html
Thank you.Do the connection (signal forwarding) in the copy constructor and assignment operator. Since you anyway want to use it in QML you can use the automatic property binding (declare a Q_PROPERTY in the wrapper class).
Yes, I can do it. It makes me omit duplicated implementation but still, I need to copy the interfaces(signals/slots/Q_INVOKABLEs).
But it seems that that's the only workaround. Thank you. -
Doesn't QML employ threads internally? I might be wrong, but I thought it did.
Yes, generally, QML runs on both of main GUI thread and scene graph rendering thread.
But the rendering thread won't appear unless you're writing some custom rendering items.
Things unrelated with rendering, all QML codes run on GUI thread, and my object lives in GUI thread only.
FYI, please see Threaded Render Loop ("threaded") section from here http://doc.qt.io/qt-5/qtquick-visualcanvas-scenegraph.html
Thank you.Do the connection (signal forwarding) in the copy constructor and assignment operator. Since you anyway want to use it in QML you can use the automatic property binding (declare a Q_PROPERTY in the wrapper class).
Yes, I can do it. It makes me omit duplicated implementation but still, I need to copy the interfaces(signals/slots/Q_INVOKABLEs).
But it seems that that's the only workaround. Thank you.@xylosper
you could also try to register a singleton type.In your case the static singleton provider would look like something this:
MySingleTonType* g_SingletonTypeInstance = 0; // global or static instance static QObject *example_qobject_singletontype_provider(QQmlEngine *engine, QJSEngine *scriptEngine) { if( g_SingletonTypeInstance == 0 ) g_SingletonTypeInstance = new MySingleTonType; return g_SingletonTypeInstance; }
-
@xylosper
you could also try to register a singleton type.In your case the static singleton provider would look like something this:
MySingleTonType* g_SingletonTypeInstance = 0; // global or static instance static QObject *example_qobject_singletontype_provider(QQmlEngine *engine, QJSEngine *scriptEngine) { if( g_SingletonTypeInstance == 0 ) g_SingletonTypeInstance = new MySingleTonType; return g_SingletonTypeInstance; }
Unfortunately, singleton is my option. Although I said 'an' instance only for example, the class could be instantiated several times. Sorry for incorrect discription.