对于QT应用程序来讲,一个插件就是一个类的实例。
插件的可用的方法由对应的接口类来决定。
接口类仅仅包含纯虚函数,也就是说接口并不实现任何方法函数。
插件通过继承QObject且继承对应的接口类来实现内部的具体函数功能。
当应用程序用QPLuginLoader类加载可能的插件时,就会得到一个指向QObject的指针。
通过将这个对象用qobject_cast 强制封装成接口类,应用程序就会分辨出插件是否真正实现了期望的接口,如果是就被加载成正确的插件。
要让QPluginLoader正确工作需要两个条件:1.插件接口类需要用宏Q_DECLARE_INTERFACE来声明;2.插件类本身也需要用Q_INTERFACES宏来声明表示实现了接口。
这两个宏能够安全地让插件类与插件接口类匹配。
事实上,QT在加载插件时有一个如下的检测标准,如果不满足,插件就不能正确加载。
检查标准列表:
- 构件插件与应用程序的Qt版本必须一致,也就是major与minor数字一样;
- 构件插件与应用程序必须在同一个平台,同一 *** 作系统,同一编译器中构建;
- Qt库配置选项在插件与应用程序中一样,且以共享模式编译。
- Q_DECLARE_INTERFACE接口类声明
比如,我们已经定义了AddInInterfaceBase接口类,可以如下声明接口类,
Q_DECLARE_INTERFACE(AddInInterfaceBase, AddInInterface_CurrentVersion)
该宏定义在qobject.h中,其实就是定义了3个模板内联函数,展开如下,
# define Q_DECLARE_INTERFACE(IFace, IId) \
template <> inline const char *qobject_interface_iid<IFace *>() \
{ return IId; } \
template <> inline IFace *qobject_cast<IFace *>(QObject *object) \
{ return reinterpret_cast<IFace *>((object ? object->qt_metacast(IId) : nullptr)); } \
template <> inline IFace *qobject_cast<IFace *>(const QObject *object) \
{ return reinterpret_cast<IFace *>((object ? const_cast<QObject *>(object)->qt_metacast(IId) : nullptr)); }
#endif
2.Q_INTERFACES插件实现接口声明
Q_INTERFACE宏定义在qobjectdef.h中
#define Q_INTERFACES(x) QT_ANNOTATE_CLASS(qt_interfaces, x)
进一步解析QT_ANNOTATE_CLASS,找到如下宏定义,
#ifndef QT_ANNOTATE_CLASS
# ifndef Q_COMPILER_VARIADIC_MACROS
# define QT_ANNOTATE_CLASS(type, x)
# else
# define QT_ANNOTATE_CLASS(type, ...)
# endif
#endif
这里可以看到只是一个空的定义,所以在源码中没有实际意义。
主要用于MOC的输入,MOC会为Q_INTERFACES动态生成一些代码,确保qobject_cast()能正确进行QObject*到接口指针的转换,下面实例中详细讲述。
3. Q_PLUGIN_METADATA插件元数据宏
#define Q_PLUGIN_METADATA(x) QT_ANNOTATE_CLASS(qt_plugin_metadata, x)
与Q_INTERFACES类似,在代码层面也是空的,也用于MOC的输入,让MOC来动态生成一些代码。
实际上,Q_PLUGIN_METADATA让MOC生成导出函数qt_plugin_instance(),供QPluginLoader()调用,创建接口实例,返回QObject的指针,在下面实例中详述。
4.MOC与Q_INTERFACES\Q_PLUG_METADATA的关系
Qt的插件机制是建立在元对象系统之上,所以要看一下Moc与Q_INTERFACES\Q_PLUGIN_METADATA两个宏之间的关系。
以OpenCVGrabber插件为例,
Q_PLUGIN_METADATA宏在自动生成的Moc文件中有IID与ClassName的信息,如下图,
查看一下,QT_MOC_EXPORT_PLUGIN的宏定义,
# define QT_MOC_EXPORT_PLUGIN(PLUGINCLASS, PLUGINCLASSNAME) \
Q_EXTERN_C Q_DECL_EXPORT \
const char *qt_plugin_query_metadata() \
{ return reinterpret_cast<const char *>(qt_pluginMetaData); } \
Q_EXTERN_C Q_DECL_EXPORT QT_PREPEND_NAMESPACE(QObject) *qt_plugin_instance() \
Q_PLUGIN_INSTANCE(PLUGINCLASS)
其中,Q_PLUGIN_INSTANCE,就是返回一个实例
#define Q_PLUGIN_INSTANCE(IMPLEMENTATION) \
{ \
static QT_PREPEND_NAMESPACE(QPointer)<QT_PREPEND_NAMESPACE(QObject)> _instance; \
if (!_instance) { \
QT_PLUGIN_RESOURCE_INIT \
_instance = new IMPLEMENTATION; \
} \
return _instance; \
}
Q_INTERFACES展开moc文件看到如下自动生成代码,
总结如下,
- Q_INTERFACES,确保能够正确类型转换;
- Q_PLUGIN_METADATA,生成插件元数据且生成qt_plugin_instance,供QPluginLoader使用;
5.QT插件加载器QPluginLoader
首先,看看它的声明比较简单
里面的instance返回对象实例指针,load()加载插件。
上面声明中,有QJsonObject的定义来描述元数据,下面简单看一下元数据的描述格式Json
6. Json与插件元数据
Json是一种存储结构化数据的一种格式,概念中与xml类似,有6中基本数据类型:
Bool, double, string, array, object与null.
Json在插件中主要存储插件的元数据信息,在Qt中有一个专门的类QJsonObject对Json描述,与下表中其他配套的类一起来对Json管理。
类名 | 功能(中文) |
QJsonArray | 封装Json数组 |
QJsonDocument | 读写Json文本 |
QJsonObject | 封装Json对象 |
QJsonObject::iterator | Json迭代器 |
QJsonObject::const_iterator | Json常迭代器 |
QJsonParseError | 报告Json解析错误 |
QJsonValue | 封装Json值 |
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)