如何将Q_GLOBAL_STATIC与插件一起使用?我想使用一个"全局"QMap,这样我就不必把QMap放在不同的地方,每次都同步。正如我所读到的,Q_GLOBAL_STATIC应该完成这项工作。
我的Qt项目有以下结构:
.
+-- 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]
主函数(在app.cpp中)是调用singleton的第一个地方。因此,它是在加载插件之前创建并已知的。
目前,我正在考虑编译singleton.o,并将生成的对象文件链接到应用程序和插件。但是如何只编译对象文件呢?
也许我必须将TEMPLATE(在common.pro中)指定为lib?如果是,它应该是共享库还是静态库?共享库没有代码副本,所以这可能是单例所需要的,对吧?
我的想法是正确的吗?我可以在不显式共享指针的情况下,只使用Q_GLOBAL_STATIC来创建一个单例吗?
现在,它使用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;
}
希望这不会导致其他问题;
可以共享全局静态实例,只是不能以您期望的方式共享。
首先,Q_GLOBAL_STATIC只是一个宏,用于创建某个具有线程安全性的类的本地实例。所以这基本上是一种奇特的方式:
static MyCass *instance() {
static auto _instance = new MyClass();
return _instance;
}
#define myGlobalStatic instance()
宏的优点是,可以保存代码,它是惰性的,并且它具有线程安全的构造函数和析构函数。换句话说,使用此宏创建本地实例使能够安全地全局使用。
这意味着,即使在不同的源文件中有相同的Q_GLOBAL_STATIC语句,也会有多个不同的实例,它们的名称都相同,但仅在本地可用。
这也适用于你的具体想法:singleton.o-然而,通过将其链接到3个不同的二进制文件(app、p1、p2),你基本上创建了3个不同实例,每个二进制文件都是本地的(因为链接器将其从对象文件复制到每个二进制文件的二进制文件中)!因此,不,您当前的想法不会共享该实例。
存档的唯一方法是创建一个包含单个实例的动态链接库,并从中导出一个getter函数,该函数提供对实例的访问。将两个插件和应用程序链接到此库,在运行时,只有一个实例(作为库的一部分),所有3个实例都可以访问。
对于你的项目,这意味着你必须把common变成一个共享的库和链接应用程序,p1和p2反对它
然而,还有第二种方法可以做到这一点,独立于Q_GLOBAL_STATIC。这个想法的唯一要求是,无论你在全球共享什么,都是在应用程序之外的其他地方定义的。使用QMap
是可行的,因为它是Qt5Core库的一部分。
其想法是,与其有一个应用程序和插件可以访问的全局实例,不如在插件接口中添加一个设置方法,将实例传递给插件:
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;
};
现在,你所要做的就是每次从你的应用程序中安装新插件时都调用setup
,你就可以开始了!您可以简单地在加载插件的源文件中拥有Q_GLOBAL_STATIC,甚至可以让它成为某个顶级类的成员。