Registering C++ objects in QML
-
[Forked from: https://forum.qt.io/topic/106926/emiting-signals-from-singletons ~kshegunov]
Just to be clear,
Database* Database::getInstance() { static Database* inst = new Database(); return inst; }
inst
will only be initialized, andnew Database()
called, once. That's been true since C. So indeed no leak.It will be initialized only the first time a thread of execution reaches its definition.
That is interesting! I wonder how they define that, and how they achieve it in generated code.
-
[Forked from: https://forum.qt.io/topic/106926/emiting-signals-from-singletons ~kshegunov]
Just to be clear,
Database* Database::getInstance() { static Database* inst = new Database(); return inst; }
inst
will only be initialized, andnew Database()
called, once. That's been true since C. So indeed no leak.It will be initialized only the first time a thread of execution reaches its definition.
That is interesting! I wonder how they define that, and how they achieve it in generated code.
-
@jonb said in Emiting signals from singletons:
I wonder how they define that, and how they achieve it in generated code.
The second implementation should be preferred in C++11 afaik
-
@jonb said in Emiting signals from singletons:
I wonder how they define that, and how they achieve it in generated code.
The second implementation should be preferred in C++11 afaik
@christian-ehrlicher said in Emiting signals from singletons:
The second implementation should be preferred in C++11 afaik
In general yes, but not for QObjects, as the static instance could outlive the application object. That is an error and can lead to crashes on shutdown, which often go unnoticed because the app is exiting anyway and thus causes subtle bugs because some destructors might not be called.
For QObjects something like this can be used:
Foo* getInstance() { static Foo* s_inst = nullptr; if (!s_inst) { s_inst = new Foo(); QObject::connect(qApp, &QCoreApplication::aboutToQuit, s_inst, &Foo::deleteLater); } return s_inst; }
or, if you don't want to pay for that
if
on every call, you can explicitly delete that singleton somewhere before the app quits, but that tends to lead to errors if you forget. -
@chris-kawa said in Emiting signals from singletons:
but not for QObjects
You're right - so connecting to aboutToQuit or use qAddPostRoutine() should be used here.
-
@chris-kawa said in Emiting signals from singletons:
but not for QObjects
You're right - so connecting to aboutToQuit or use qAddPostRoutine() should be used here.
@christian-ehrlicher said in Emiting signals from singletons:
You're right - so connecting to aboutToQuit or use qAddPostRoutine() should be used here.
Yeah, you just have to be careful with
qAddPostRoutine()
if your singleton lives in a dynamically loaded library, as the cleanup code could try to call something from an unloaded lib.Another problem with this is that this
getInstance()
can be called beforeqApp
is created or after it is destroyed (often happens in destructors). I would add an extra check to see ifqApp
is null and returnnullptr
, add an assertion, throw an exception or do whatever your favorite way of handling errors is. -
@jonb said in Emiting signals from singletons:
I wonder how they define that, and how they achieve it in generated code.
The second implementation should be preferred in C++11 afaik
@christian-ehrlicher
I looked at the generated code you linked to. very interesting: as i suspected, it implements "first time executed only" via a "guard variable" :) -
@christian-ehrlicher said in Emiting signals from singletons:
The second implementation should be preferred in C++11 afaik
In general yes, but not for QObjects, as the static instance could outlive the application object. That is an error and can lead to crashes on shutdown, which often go unnoticed because the app is exiting anyway and thus causes subtle bugs because some destructors might not be called.
For QObjects something like this can be used:
Foo* getInstance() { static Foo* s_inst = nullptr; if (!s_inst) { s_inst = new Foo(); QObject::connect(qApp, &QCoreApplication::aboutToQuit, s_inst, &Foo::deleteLater); } return s_inst; }
or, if you don't want to pay for that
if
on every call, you can explicitly delete that singleton somewhere before the app quits, but that tends to lead to errors if you forget.This post is deleted! -
@stehlo The
aboutToQuit
signal is meant for exactly such last minute cleanup and should definitely not be avoided. It's possible that there's bug in Qt itself, however I suspect the error is more likely to be in your code. In either case I'd debug the thing first before jumping to conclusions and no matter where the bug is - fix it or report a bug in Qt.Can you provide a minimal example that exhibits this problem or a stack trace of the crash from your app?
-
@stehlo The
aboutToQuit
signal is meant for exactly such last minute cleanup and should definitely not be avoided. It's possible that there's bug in Qt itself, however I suspect the error is more likely to be in your code. In either case I'd debug the thing first before jumping to conclusions and no matter where the bug is - fix it or report a bug in Qt.Can you provide a minimal example that exhibits this problem or a stack trace of the crash from your app?
This post is deleted! -
Actually the "best" singleton is one that you don't create. When I need the global scope I usually do what
QCoreApplication
does:class MyGlobalClass : public QObject { static MyGlobalClass * p; public: inline MyGlobalClass(QObject * parent) : QObject(parent) { Q_ASSERT(!p); p = this; } ~MyGlobalClass() override { p = nullptr; ] inline static MyGlobalClass * instance() { // Q_ASSERT(p); can be added optionally. return p; } }; MyGlobalClass * MyGlobalClass::p = nullptr;
and I create it where it's supposed to go - in the stack (somewhere):
int main(int argc, char ** argv) { QCoreApplication app; MyGlobalClass theObject(&app); // ... return ... }
I continue to claim that a "singleton" is nothing more than a glorified global variable, so it should be treated exactly as such - a
badmonstrously stupid idea in 99.999% of cases! -
Actually the "best" singleton is one that you don't create. When I need the global scope I usually do what
QCoreApplication
does:class MyGlobalClass : public QObject { static MyGlobalClass * p; public: inline MyGlobalClass(QObject * parent) : QObject(parent) { Q_ASSERT(!p); p = this; } ~MyGlobalClass() override { p = nullptr; ] inline static MyGlobalClass * instance() { // Q_ASSERT(p); can be added optionally. return p; } }; MyGlobalClass * MyGlobalClass::p = nullptr;
and I create it where it's supposed to go - in the stack (somewhere):
int main(int argc, char ** argv) { QCoreApplication app; MyGlobalClass theObject(&app); // ... return ... }
I continue to claim that a "singleton" is nothing more than a glorified global variable, so it should be treated exactly as such - a
badmonstrously stupid idea in 99.999% of cases!This post is deleted! -
This post is deleted!
@stehlo said in Emiting signals from singletons:
This is an inappropriate statement because I have specifically mentioned that I arrived at the fix after an extensive testing session. I have most definitely not jumped to any conclusions out of the blue.
It is a correct statement since, as @Chris-Kawa already mentioned a correct way to clean up stuff before the application ends and it works correctly. So the problem must be on your side but since you don't provide code we can't help.
-
Deleting the topic for sure helps to fix the problems...
-
Deleting the topic for sure helps to fix the problems...
This post is deleted! -
This post is deleted!
@stehlo said in Emiting signals from singletons:
How do you use such a construct as an object in QML?
As a context property?
-
This post is deleted!
@stehlo It is your right to use the support that fits you best.
But once you asked in public, and many people tried to help you, these answers should stay public.
So I've restored the post, and I ask to to keep it that way. Thanks!
-
@stehlo It is your right to use the support that fits you best.
But once you asked in public, and many people tried to help you, these answers should stay public.
So I've restored the post, and I ask to to keep it that way. Thanks!
This post is deleted! -
This post is deleted!
@stehlo said in Registering C++ objects in QML:
Therefore, I insist on the removal of this tainted topic without any further discussions.
It'd been forked in its own topic. Which should satisfy your request, shouldn't it?
-
Wow this escalated quickly!
I think the fork is a good idea, tbh.
@kshegunov said in Registering C++ objects in QML:
I continue to claim that a "singleton" is nothing more than a glorified global variable, so it should be treated exactly as such - a bad monstrously stupid idea in 99.999% of cases!
sadly, in combination with QML your options are limited. If you want to access the class from cpp and QML you can do one of the following
- Singleton, instance in cpp and exactly where you need it in qml
- Normal instance in cpp, a global property accessible in all qml files
- using find child to search for your qml item and do the connect in cpp. This falls apart as soon as you start to dynamically load your qml files
So, Pest Cholera, or Malaria.
@stehlo said in Registering C++ objects in QML:
Without going into extra detail which would detract from the main point, the attempt to deleteLater the QObject unnaturally at the point of the application being aboutToQuit causes either of the two following fatal errors, depending on whether we do this for the networking singleton or the database one:
QObject: shared QObject was deleted directly. The program is malformed and may crash.
abort_message: assertion "Pure virtual function called!" failed
Fatal signal 6 (SIGABRT), Abortedor
null pointer dereference
Fatal signal 11 (SIGSEGV), Segmentation faultThe attempt to deleteLater is happening either too early or two late.
I actually have to disagree.
after reading the post by @Chris-Kawa I changed my own project to include the about to quit cleanup. That's for about 10 Singletons not once did I have one of your errors mentions above.I would say there's still something going on that causes your issue, but I think it's user -rather than framework related