QT插件机制中宏Q_PLUGIN_METADATA和Q_INTERFACES的作用
今天简单地了解了一下QT的插件机制,其实也没有什么可多说的,理论上说到底无非就是库的加载和函数地址的查找。但是QT对于插件机制的支持是建立在它独有的元对象系统基础之上,自然也有自己的一套插件实现规则,或者说是语法。
一般来讲,照着QT给的文档和DEMO,很容易就能实现一个插件,这里面最主要的就是三个宏:
Q_DECLARE_INTERFACE
Q_PLUGIN_METADATA
Q_INTERFACES
那么,这三个宏到底在整个插件机制中到底起到一个以什么作用呢?
Q_DECLARE_INTERFACE宏好理解,就是定义了接口ID查找函数和几个QObject到接口的转换函数:
#if !defined(Q_MOC_RUN) && !defined(Q_CLANG_QDOC)
# 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 // Q_MOC_RUN
Q_PLUGIN_METADATA和Q_INTERFACES定义如下:
#define Q_PLUGIN_METADATA(x) QT_ANNOTATE_CLASS(qt_plugin_metadata, x)
#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
所以,Q_PLUGIN_METADATA和Q_INTERFACES应该是Moc.exe自己做了解释处理. 我在本地的一个插件的头文件的Moc文件中发现了一下内容:
QT_PLUGIN_METADATA_SECTION
static constexpr unsigned char qt_pluginMetaData[] = {'Q', 'T', 'M', 'E', 'T', 'A', 'D', 'A', 'T', 'A', ' ', '!',// metadata version, Qt version, architectural requirements0, QT_VERSION_MAJOR, QT_VERSION_MINOR, qPluginArchRequirements(),0xbf, // "IID"0x02, 0x77, 'o', 'r', 'g', '.', 'k', 'o', 'm', 'm', 'a', 'n', 'd', 'e', 'r', '.', 'I', 'n', 't', 'e', 'r', 'f', 'a', 'c', 'e', // "className"0x03, 0x70, 'M', 'o', 'u', 's', 'e', 'C', 'l', 'i', 'c', 'k', 'P', 'l', 'u', 'g', 'i', 'n', 0xff,
};
QT_MOC_EXPORT_PLUGIN(MouseClickPlugin, MouseClickPlugin)QT_WARNING_POP
QT_END_MOC_NAMESPACE
这里重要的是倒数第三行的那个宏:QT_MOC_EXPORT_PLUGIN,看一下定义:
#if defined(QT_STATICPLUGIN)# define QT_MOC_EXPORT_PLUGIN(PLUGINCLASS, PLUGINCLASSNAME) \static QT_PREPEND_NAMESPACE(QObject) *qt_plugin_instance_##PLUGINCLASSNAME() \Q_PLUGIN_INSTANCE(PLUGINCLASS) \static const char *qt_plugin_query_metadata_##PLUGINCLASSNAME() { return reinterpret_cast<const char *>(qt_pluginMetaData); } \const QT_PREPEND_NAMESPACE(QStaticPlugin) qt_static_plugin_##PLUGINCLASSNAME() { \QT_PREPEND_NAMESPACE(QStaticPlugin) plugin = { qt_plugin_instance_##PLUGINCLASSNAME, qt_plugin_query_metadata_##PLUGINCLASSNAME}; \return plugin; \}#else# 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)
看#else部分,定义了两个导出函数,qt_plugin_query_metadata()和qt_plugin_instance(),调试QPluginLoader的源码,发现qt_plugin_instance()正是QPluginLoader实例化插件对象实例时调用的函数.
所以,Q_PLUGIN_METADATA的作用就是生成导出函数qt_plugin_instance()供QPluginLoader查找并调用生成接口实例。当然,还生成了返回插件元数据的函数。
再说Q_INTERFACES。 从动态库的层面来讲,有了Q_PLUGIN_METADATA,QPluginLoader就能加载插件(LoadLibrary),并正确查找(GetProcessAddress)并调用qt_plugin_instance()生成实例供外界调用了,为什么还要加上Q_INSTANCES?
试着对比了一下有Q_INSTANCES和没有Q_INSTANCES生成出来的moc文件,发现了以下差异:
没错,有Q_INSTANCES的moc文件中,qt_metacast()多了一条判断语句,如果转换目标对象的名字是我们目标接口的IID时,则进行相应的转换操作,并返回接口指针,否则将返回空。
这条语句到底有什么用?
注意前面的qt_plugin_instance()定义,返回的是一个QObject*. 我们通过QPluginLoader获得一格QObject之后,还需要判断这个QObject有没有实例化目标接口? 一般会调用qobject_cast()进行转换,而qobject_cast()最终就是调用这里的qt_metacast()。
---------总结---------
Q_PLUGIN_METADATA 让moc生成导出函数qt_plugin_instance(),供QPluginLoader()调用,创建接口实例,不过返回的是一个QObject*.
Q_INTERFACES 让qobject_cast()能正确进行QObject*到接口指针的转换,借此,我们可以判断插件合法性。
QT插件机制中宏Q_PLUGIN_METADATA和Q_INTERFACES的作用相关推荐
- LINUX 宏__define_initcall(level,fn)的作用 和 do_initcalls()
前言 宏定义__define_initcall(level,fn)对于内核的初始化很重要,它指示 编译器在编译的时候,将一系列初始化函数的起始地址值按照一定的顺序 放在一个section中.在内核初始 ...
- C语言宏定义中#号的作用
C语言宏定义中#号的作用 前言 #号的作用 前言 最近,在阅读uboot的源码过程中,发现了一段宏定义代码: #define U_BOOT_CMD_MKENT_COMPLETE(_name, _max ...
- QT之Qt之Q_PROPERTY宏理解
在初学Qt的过程中,时不时地要通过F2快捷键来查看QT类的定义,发现类定义中有许多Q_PROPERTY的东西,比如最常用的QWidget的类定义: Qt中的Q_PROPERTY宏在Qt中是很常用的,那 ...
- Qt中Q_D宏及d指针
原文标题:d指针在Qt上的应用及实现 原文链接:http://blog.csdn.net/rabinsong/article/details/9474859 正文: Qt为了使其动态库最大程度上实现二 ...
- 【Qt】获取、比较Qt版本的宏和函数
1.版本号宏定义 版本号宏定义在QtCore\qconfig.h中,以Qt5.14.2为例 #define QT_VERSION_STR "5.14.2" #define QT_V ...
- Qt使用Q_UNUSED宏处理不使用的形参
对于没有使用的一个参数arg1,会提示警告unused parameter 'arg1' 利用 Q_UNUSED(arg1); 规避警告 void MainWindow::on_m_ip_lineEd ...
- Qt插件机制介绍及实现
Qt插件机制介绍及实现 创建应用程序主窗口 创建Qt项目 编辑项目文件ImageView.pro mainwindow.cpp main.cpp mainwindow.cpp 编译运行 插件接口 实现 ...
- 【Qt】Qt数据库驱动层
00. 目录 文章目录 00. 目录 01. Qt驱动层 02. QSqlDriver 03. QSqlDriverCreator 04. QSqlDriverCreatorBase 05. QSql ...
- 为QT的Webkit 编写插件
为了允许的QWebView加载插件,必须使能QWebView的Javascript和Plugins属性,使能方法为: QWebSettings::globalSettings()->setAtt ...
最新文章
- MindSpore技术理解(下)
- Java数据访问对象模式
- 通俗易懂:图卷积神经网络入门详解
- 黑科技,教你用Python打电话,控制手机技术,快来学一下
- 18.self关键字.rs
- java http2_探索HTTP/2: HTTP 2协议简述(原)
- jQuery环境搭建
- 苹果发布iOS 13.6.1更新,iPhone不会再变绿了
- mask rcnn算法分析_实例分割综述(单阶段/两阶段/实时分割算法汇总)
- Remote Desktop Connection for mac 报错:证书或相关链无效
- paip.mysql error2003 Can''t connect to MySQL server on localhost (10061)的解决
- pythonnet 引用_Python netmiko模块的使用
- 西电计科《算法分析与设计》上机(源码+实验报告+历次作业)(渗透问题+排序算法性能比较+地图路由+文本索引)(2019级 霍红卫老师)
- matlab的数值求解实验报告,偏微分方程数值及matlab实验报告
- 个人主页博客网页设计制作HTML5+CSS大作业——清新春暖花开个人博客网站(6页)
- 从一加到100等于多少c语言,从一加到99等于多少
- mysql建表auto_increment_mysql create table auto_increment
- 头哥实践教学平台 CC++程序设计(计算机程序设计)基本输入输出
- Studio3t 过期激活办法/以及重新设置使用日期的脚本不可用解决办法/Studio 3T无限激活原创
- h5 修改title 微信_h5制作小程序 邀请函模板免费
热门文章
- 开发板通过tftp服务 下载Ubuntu上的文件
- html引入babel-polyfill,babel-polyfill
- win32打印机控制,API打印操作
- PPT制作经验总结(PPT制作七条原则)
- UE5出现:File:D:\build\++UE5\Sync\Engine\Source\Runtime\Windows\D3D11RHI\Private\D3D11Util.cpp
- click事件和onclick事件的区别
- 一位阿里软件测试工程师的35岁职业规划,他为什么没有被裁?
- 03-Redis客户端连接Redis服务器(redis.conf 文件配置没有生效导致redis运行报错Error: Connection reset by peer)
- c语言 输入一个以回车结束的字符串(少于80个字符),过滤掉所有的非十六进制数后,组成一个新的字符串(十六进制形式),输出该字符串并将其转换为十进制输出
- GPS术语 -- 词汇与概念解释(三)