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


  • Qt Champions 2016

    You are passing a reference to a stack variable to QGuiApplication, which gets invalidated when you return from the function. Notice that argc should be passed as int & ... 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?


  • Qt Champions 2016

    @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 already QGuiApplication.



  • @kshegunov

    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:)


  • Qt Champions 2016

    I don't follow, if you will have plugins that depend on widgets, then you will need the QtWidgets module (and by extension QApplication) anyway ... and you're complicating your life on a "might" ... doesn't make much sense to me.



  • @kshegunov

    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:
    alt text
    Link to the image:
    https://i.imgsafe.org/5e6f40aa3d.png
    https://s2.postimg.org/ei5411mkp/My_App.png

    The 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 will might 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().


  • Qt Champions 2016

    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 QObjects before QCoreApplication (by means of the plugin's QObject entry point) which is unsupported.



  • @kshegunov

    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.


  • Qt Champions 2016

    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 QObjects 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.



  • @kshegunov

    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.


  • Qt Champions 2016

    @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();
    }
    


  • @kshegunov

    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):

    1. 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.

    2. Packaging Qt libs and app in the APK.

    But it's too early for me to decide which one to choose.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.