Creating QGuiApplication on the heap - crashes (multiple UIs via plugins)
-
Hi, I need to create QGuiApplication on the heap. I follow the example from
https://doc.qt.io/qt-5/qapplication.html#details that creates QApplication in a separate function but in my function I want to return std::unique_ptr<QGuiApplication> instead of a raw QGuiApplication*.What I encountered are some strange looking errors depending on the way I create QGuiApplication.
I've tried to create a minimal working program to show my problem:
#include <memory> #include <QDebug> #include <QGuiApplication> std::unique_ptr<QGuiApplication> createApplication1(int argc, char *argv[]) { // works std::unique_ptr<QGuiApplication> ptr(new QGuiApplication(argc,argv)); return ptr; } std::unique_ptr<QGuiApplication> createApplication2(int argc, char *argv[]) { // works std::unique_ptr<QGuiApplication> ptr; ptr.reset(new QGuiApplication(argc,argv)); return ptr; } std::unique_ptr<QGuiApplication> createApplication3(int argc, char *argv[]) { // app->exec() crashes randomly std::unique_ptr<QGuiApplication> ptr; QGuiApplication* app = new QGuiApplication(argc,argv); ptr.reset(app); return ptr; } std::unique_ptr<QGuiApplication> createApplication4(int argc, char *argv[]) { // app->exec() always crashes return std::make_unique<QGuiApplication>(argc,argv); } int main(int argc, char *argv[]) { auto app = createApplication3(argc,argv); // auto app2 = std::make_unique<QGuiApplication>(argc,argv); // crashes with createApplication3 and createApplication4, but works with app2 return app->exec(); }
The strange thing is the difference between createApplication2() and createApplication3() is minor (an addition of a named raw pointer) but the latter makes app->exec() crash randomly. Also, all these variants work when used directly in main() instead of a separate function, e.g. this works in main:
auto app2 = std::make_unique<QGuiApplication>(argc,argv);
but not in createApplication4().
When the program crashes the stack trace is:
1 __GI_raise raise.c 58 0x7ffff5cd6fdf 2 __GI_abort abort.c 89 0x7ffff5cd840a 3 __gnu_cxx::__verbose_terminate_handler() 0x7ffff65ee07d 4 ?? 0x7ffff65ec046 5 std::terminate() 0x7ffff65ec091 6 __cxa_throw 0x7ffff65ec2a9 7 qBadAlloc() 0x7ffff6dfe9b2 8 QListData::detach(int) 0x7ffff6e7cbba 9 ?? 0x7ffff6e284de 10 QCoreApplication::arguments() 0x7ffff6fefd2f 11 ?? 0x7fffef97deb7 12 ?? 0x7fffef97ea04 13 _SmcProcessMessage 0x7fffef0eae45 14 IceProcessMessages 0x7fffeeeda33c 15 QMetaObject::activate(QObject *, int, int, void * *) 0x7ffff70185e9 16 QSocketNotifier::activated(int, QSocketNotifier::QPrivateSignal) 0x7ffff70925ce 17 QSocketNotifier::event(QEvent *) 0x7ffff7024ba2 18 QCoreApplication::notify(QObject *, QEvent *) 0x7ffff6fec87a 19 QCoreApplication::notifyInternal2(QObject *, QEvent *) 0x7ffff6fec9e0 20 ?? 0x7ffff70414fd 21 g_main_context_dispatch 0x7ffff44587f7 22 ?? 0x7ffff4458a60 23 g_main_context_iteration 0x7ffff4458b0c 24 QEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) 0x7ffff704104f 25 QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) 0x7ffff6fea9ca 26 QCoreApplication::exec() 0x7ffff6ff313c 27 main main.cpp 48 0x555555556488
Thanks
-
You are passing a reference to a stack variable to
QGuiApplication
, which gets invalidated when you return from the function. Notice thatargc
should be passed asint &
... not that I have any idea why you would want to create the application object in the heap ... -
Thanks for your help kshegunov. Passing a reference to a local stack variable to QGuiApplication was indeed the source of the problem. I was misled by the fact that createApplication1() and createApplication2() worked flawlessly even with this error.
"valgrind --leak-check=full" doesn't show anything, but valgrind isn't probably the best tool for stack-related errors . Maybe sanitizers could be helpful to detect this kind of bug.
I'm creating an application with both Qt Widgets and Qt Quick Controls UI.
My idea is to put both UIs into separate plugins which the main application starts. I was thinking about letting the plugins create a QGuiApplication / QApplication instance in a createApplication() function so the main app doesn't have to know the details (maybe it could even work with KApplication?).
The alternative could be to create a QGuiApplication / QApplication in the main app based on the id (e.g. QString or enum) retrieved from the chosen plugin. This might have some limitations.
Are there any better solutions?
-
@Deckard said in Creating QGuiApplication on the heap - crashes (multiple UIs):
The alternative could be to create a QGuiApplication / QApplication in the main app based on the id (e.g. QString or enum) retrieved from the chosen plugin. This might have some limitations.
Well, just creating one
QApplication
as one usually does should be just enough,QApplication
is alreadyQGuiApplication
. -
The documentation for QApplication:
https://doc.qt.io/qt-5/qapplication.html#details
says it depends on QtWidgets.I would like to avoid this in the Qt Quick version as it might ruin start-up time:)
-
I don't follow, if you will have plugins that depend on widgets, then you will need the
QtWidgets
module (and by extensionQApplication
) anyway ... and you're complicating your life on a "might" ... doesn't make much sense to me. -
I admit I'm not an expert so I may be wrong. I will try to explain my idea.
Here is a simplified view of my application:
Link to the image:
https://i.imgsafe.org/5e6f40aa3d.png
https://s2.postimg.org/ei5411mkp/My_App.pngThe idea is that UI plugins are optional and only one is needed at the time. If I load ONLY MyAppQtQuick plugin, the QtWidgets will not get loaded (hopefully) because QGuiApplication that is created in MyAppQtQuick doesn't depend on QtWidgets.
This will not be useful in Qt based environments like KDE or LXQt because QtWidgets are always loaded there but it might be useful in environments like GNOME where QtWidgets is not always loaded (because no other Qt application is running). So starting QtWidgets in addition to QtQuick
willmight affect start-up time in GNOME.There is also a packaging issue. The app will be free software and I'd like it to be packaged (hopefully) by distributors in way that allows the user selecting only needed plugins. Again, if someone is using GNOME and wants to use only MyAppQtQuick plugin, he can avoid downloading QtWidgets package if MyAppQtQuick creates QGuiApplication. But if I create QApplication in MyApp instead, then it will always depend on QtWidgets, whatever plugin is used.
And for Android or other mobile systems QtWidgets are not useful.But you made me thinking if all plugins are not loaded anyway even I'm looking for only one with particular ID. This would be a common way of loading a particular plugin:
MyPluginType* loadPlugin(const QString& pluginId) { QDir dir ("/usr/lib/myapp"); for (QString fileName : dir.entryList(QDir::Files)) { auto path = dir.absoluteFilePath(fileName); QPluginLoader loader(path); QObject *somePlugin = loader.instance(); // loads a plugin; are plugin's dependencies loaded too? if (somePlugin) { if (auto myPlugin = qobject_cast<MyPluginType*>(somePlugin)) { if ( myPlugin->name() == pluginId) return myPlugin; } loader.unload(); // unloading a plugin } } return nullptr; }
So if both plugins are installed and I want to load only a plugin with pluginId == "QtQuick" I will have to iterate over all files in the given folder, load them one by one to check if they have desired ID and unload them if not. QPluginLoader::instance() likely loads all plugin's dependencies.
But the files in QDir::entryList() can be sorted in any order so MyAppQtWidgets plugin might be loaded (likely with its dependencies e.g. QtWidgets) before MyAppQtQuick plugin, only to discard it and unload.
So to summarize: loading plugins selectively based on the retreived id is probably not the best idea because I have to load a plugin (with its dependencies) first to check its id. Another option would be to just blacklist or whitelist plugin file name but this may be error prone.
What I'm writing may be completely wrong of course...
EDIT: Ok, I've found something that looks like a good solution: I can get plugin's info without loading it using QPluginLoader::metaData().
-
Well, yes, there's that. But I still doubt the wisdom in delegating the responsibility to create the application to the plugins. You also run into the trouble of creating
QObject
s beforeQCoreApplication
(by means of the plugin'sQObject
entry point) which is unsupported. -
I understand that, without QCore/QGUI/QApplication, events and singals/slots don't work. But in the documentation for QObject, QCoreApplication, QGuiApplication, QApplication and QPluginLoader I haven't found any warning that creating QObjects before QCoreApplication is not supported, dangerous, illegal or leads to e.g. undefined behaviour. It is only recommended to create the application object as early as possible and QApplication must be created before any QPaintDevice objects, e.g. QWidgets. If I'm wrong, please correct me.
And if I'm wrong about this, another option is to split my application into two executables with different names, both dependent on different UIs. I wanted to avoid that because it adds some work to maintain separate files for each version for desktop environment / file manager integration.
-
In general, creating QObjects before the QApplication is not supported and can lead to weird crashes on exit, depending on the platform. This means static instances of QObject are also not supported. A properly structured single or multi-threaded application should make the QApplication be the first created, and last destroyed QObject.
From here and on the the mailing list. There are multiple posts for
QObject
s not working and/or crashing when created as static variables (i.e. before the application object) but I'm too lazy to dig them all up.And if I'm wrong about this, another option is to split my application into two executables with different names, both dependent on different UIs.
As I said, just create one
QApplication
and roll with it. If you're so worried about the time it takes to initialize the widget stuff, benchmark it and only then decide if you need another solution. If you still insist on creating a separate application object for each UI, then pass an argument on startup and create the corresponding application object based on that. -
Ok, so I have no other choice then to create an application object first. Otherwise I will end up with a malfunctioning application.
Regarding the Qt Widgets dependency, I'd rather go with splitting my app into two parts. It's not about start up time - the difference may be negligible indeed.
It's about additional size (6.4 MiB on amd64 for Qt Widgets). If I understand correctly I have to deploy all needed Qt libs with my application for Android. So every time I update my app users have to also download all included Qt libs, including ~6.4 MiB for Qt Widgets which are redundant. On mobile devices it makes a difference - connection is slow, costly and drains battery, especially on 3G). Also, if all Qt-based apps are deployed that way they waste disk space.
Or, maybe I'm just oversensitive. Or wrong again:)
Yet another option would be a conditional, macro-based compilation to switch to QGuiApplication when compiling for Android. Maybe as a last resort...
Anyway, thank you very much for helping me out! Understanding the QApplication/QObject problem has been very important and saved me a frustration later in development.
-
@Deckard said in Creating QGuiApplication on the heap - crashes (multiple UIs via plugins):
So every time I update my app users have to also download all included Qt libs, including ~6.4 MiB for Qt Widgets which are redundant.
Why? Are you going to do static linking? If not what's the problem with only updating your app without updating the Qt binaries. Additionally, one of the very fine things I like about Qt is that it's binary compatible, which means you can even update the Qt binaries without deploying your app (assuming you don't use new-version features. This actually enables you to fetch patched Qt versions without modifying your app (or redeploying it).
if all Qt-based apps are deployed that way they waste disk space.
Well, the best way to deploy on Linux (I don't know much about android) is to not deploy Qt at all, but rather use the one from the distro's repository.
Yet another option would be a conditional, macro-based compilation to switch to QGuiApplication when compiling for Android. Maybe as a last resort...
You can setup this rather easily:
android-*: DEFINES += ANDROID_BUILD else: QT += widgets
in the pro file and then you can typedef the class in main (for brevity):
#ifdef ANDROID_BUILD #include <QGuiApplication> typedef QGuiApplication Application; #else #include <QApplication> typedef QApplication Application; #endif int main(int argc, char ** argv) { Application app(argc, argv); return Application::exec(); }
-
Yes, I know that on Linux package managers will install dependencies automatically, however I was worried about Android,
Fortunately there are two ways of deploying Qt-based apps for Android (blog post):
-
Using Ministro service, which will install needed Qt libs on demand. The libs are shared between applications. Ministro is available at Google Play and F-Droid.
-
Packaging Qt libs and app in the APK.
But it's too early for me to decide which one to choose.
-