Reputation: 63
How to use Q_GLOBAL_STATIC with plugins? I want to use a "global" QMap over all, so that I don't have to hold the QMap in different places and sync it every time. As I've read, Q_GLOBAL_STATIC should do the job.
My Qt-project has the following structure:
.
+-- common
| +-- common.pro
| +-- singleton.h
| +-- singleton.cpp [contains Q_GLOBAL_STATIC and returns it via instance() static method]
+-- plugins
| +-- p1
| | +-- p1.pro
| | +-- ...
| +-- p2
| | +-- p2.pro
| | +-- ...
| +-- plugins.pro [TEMPLATE = subdirs]
+-- app
| +-- app.pro
| +-- app.cpp
| +-- ...
+-- project.pro [TEMPLATE = subdirs]
The main function (in app.cpp) is the first place calling the singleton. So it is created and known before a plugin is loaded.
Currently I'm thinking to compile singleton.o and link the resulting object file to app and the plugins. But how to compile only the object file?
Maybe I have to specify the TEMPLATE (in common.pro) as lib? If so, should it be a shared or static lib? Shared lib has no copy of code, so maybe this is needed for the singleton over all, right?
Are my thoughts right and can I have a singleton over all without sharing the pointer explicitly, only by using Q_GLOBAL_STATIC?
Upvotes: 1
Views: 2947
Reputation: 63
Now it works with a little modification in the instance method using qApp.
/* [singleton.h] */
class Singleton : public QObject {
/* ... */
};
Q_DECLARE_OPAQUE_POINTER(Singleton *)
Q_DECLARE_METATYPE(Singleton *)
/* [singleton.cpp] */
Singleton * Singleton::instance() {
static Singleton * __ptr(NULL);
if (!qApp->property("singletonkey").isValid() && !__ptr) {
__ptr = ptr_from_macro; // Q_GLOBAL_STATIC pointer
qApp->setProperty("singletonkey", QVariant::fromValue(__ptr));
} else if (!__ptr) {
__ptr = qvariant_cast<Singleton *>(qApp->property("singletonkey"));
} else if (!qApp->property("singletonkey").isValid()) {
qApp->setProperty("singletonkey", QVariant::fromValue(__ptr));
}
return __ptr;
}
Hope that this doesn't lead to other problems 🙈
Upvotes: 1
Reputation: 7146
It is possible to share the global static instance, just not in a way you expect it to.
Firstoff, Q_GLOBAL_STATIC is simply a macro to create a local instance of a certain class with threadsafety. So it is basically a fancy way of doing:
static MyCass *instance() {
static auto _instance = new MyClass();
return _instance;
}
#define myGlobalStatic instance()
The advantages of the macro are, that you save code, that it is lazy and that it has threadsafe constructors and destructors. In other words creating a local instance with this macro enables it to be safely used globally.
This implies that even if you have the same Q_GLOBAL_STATIC statement in different source files, there will be multiple different instances, all named the same, but only available locally.
This also applies for your concrete idea: There will be a single such instance inside singleton.o - however, by linking it to 3 different binaries (app, p1, p2), you basically create 3 different instances, local to each of the binaries (as the linker copies it from the object file into the binary for each of them)! So no, your current idea will not share the instance.
The only way to archive this is to create a dynamically linked library that contains the single instance and export a getter function from it that provides access to the instance. Link the two plugins and the app against this library, and at runtime, there will be only 1 single instance (as part of the library), that all 3 can access.
For your project, this means you have to turn common into a shared library and link app, p1 and p2 against it.
However, there is also a second way to do this, independently of Q_GLOBAL_STATIC. The only requirement for this idea is, that whatever it is that you share globally, is defined somewhere else but the application. Using a QMap
would work, as it is part of the Qt5Core library.
The idea is to instead of having a global instance the appliation and plugins can access, add a setup method to the plugin interface that passes the instance to the plugins:
class MyPlugin
{
Q_DISABLE_COPY(MyPlugin)
public:
MyPlugin() = default;
virtual ~MyPlugin() = default;
// This is the important part: have a setup method
virtual void setup(QMap<QString, int> &globalMap) = 0;
};
Now, all you have to do is call setup
every time you instanciate a new plugin from your app and your good to go! You can simply have the Q_GLOBAL_STATIC in the source file that loads the plugins, or even have it be a member of some top-level class.
Upvotes: 0