今天简单地了解了一下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的作用相关推荐

  1. LINUX 宏__define_initcall(level,fn)的作用 和 do_initcalls()

    前言 宏定义__define_initcall(level,fn)对于内核的初始化很重要,它指示 编译器在编译的时候,将一系列初始化函数的起始地址值按照一定的顺序 放在一个section中.在内核初始 ...

  2. C语言宏定义中#号的作用

    C语言宏定义中#号的作用 前言 #号的作用 前言 最近,在阅读uboot的源码过程中,发现了一段宏定义代码: #define U_BOOT_CMD_MKENT_COMPLETE(_name, _max ...

  3. QT之Qt之Q_PROPERTY宏理解

    在初学Qt的过程中,时不时地要通过F2快捷键来查看QT类的定义,发现类定义中有许多Q_PROPERTY的东西,比如最常用的QWidget的类定义: Qt中的Q_PROPERTY宏在Qt中是很常用的,那 ...

  4. Qt中Q_D宏及d指针

    原文标题:d指针在Qt上的应用及实现 原文链接:http://blog.csdn.net/rabinsong/article/details/9474859 正文: Qt为了使其动态库最大程度上实现二 ...

  5. 【Qt】获取、比较Qt版本的宏和函数

    1.版本号宏定义 版本号宏定义在QtCore\qconfig.h中,以Qt5.14.2为例 #define QT_VERSION_STR "5.14.2" #define QT_V ...

  6. Qt使用Q_UNUSED宏处理不使用的形参

    对于没有使用的一个参数arg1,会提示警告unused parameter 'arg1' 利用 Q_UNUSED(arg1); 规避警告 void MainWindow::on_m_ip_lineEd ...

  7. Qt插件机制介绍及实现

    Qt插件机制介绍及实现 创建应用程序主窗口 创建Qt项目 编辑项目文件ImageView.pro mainwindow.cpp main.cpp mainwindow.cpp 编译运行 插件接口 实现 ...

  8. 【Qt】Qt数据库驱动层

    00. 目录 文章目录 00. 目录 01. Qt驱动层 02. QSqlDriver 03. QSqlDriverCreator 04. QSqlDriverCreatorBase 05. QSql ...

  9. 为QT的Webkit 编写插件

    为了允许的QWebView加载插件,必须使能QWebView的Javascript和Plugins属性,使能方法为: QWebSettings::globalSettings()->setAtt ...

最新文章

  1. MindSpore技术理解(下)
  2. Java数据访问对象模式
  3. 通俗易懂:图卷积神经网络入门详解
  4. 黑科技,教你用Python打电话,控制手机技术,快来学一下
  5. 18.self关键字.rs
  6. java http2_探索HTTP/2: HTTP 2协议简述(原)
  7. jQuery环境搭建
  8. 苹果发布iOS 13.6.1更新,iPhone不会再变绿了
  9. mask rcnn算法分析_实例分割综述(单阶段/两阶段/实时分割算法汇总)
  10. Remote Desktop Connection for mac 报错:证书或相关链无效
  11. paip.mysql error2003 Can''t connect to MySQL server on localhost (10061)的解决
  12. pythonnet 引用_Python netmiko模块的使用
  13. 西电计科《算法分析与设计》上机(源码+实验报告+历次作业)(渗透问题+排序算法性能比较+地图路由+文本索引)(2019级 霍红卫老师)
  14. matlab的数值求解实验报告,偏微分方程数值及matlab实验报告
  15. 个人主页博客网页设计制作HTML5+CSS大作业——清新春暖花开个人博客网站(6页)
  16. 从一加到100等于多少c语言,从一加到99等于多少
  17. mysql建表auto_increment_mysql create table auto_increment
  18. 头哥实践教学平台 CC++程序设计(计算机程序设计)基本输入输出
  19. Studio3t 过期激活办法/以及重新设置使用日期的脚本不可用解决办法/Studio 3T无限激活原创
  20. h5 修改title 微信_h5制作小程序 邀请函模板免费

热门文章

  1. 开发板通过tftp服务 下载Ubuntu上的文件
  2. html引入babel-polyfill,babel-polyfill
  3. win32打印机控制,API打印操作
  4. PPT制作经验总结(PPT制作七条原则)
  5. UE5出现:File:D:\build\++UE5\Sync\Engine\Source\Runtime\Windows\D3D11RHI\Private\D3D11Util.cpp
  6. click事件和onclick事件的区别
  7. 一位阿里软件测试工程师的35岁职业规划,他为什么没有被裁?
  8. 03-Redis客户端连接Redis服务器(redis.conf 文件配置没有生效导致redis运行报错Error: Connection reset by peer)
  9. c语言 输入一个以回车结束的字符串(少于80个字符),过滤掉所有的非十六进制数后,组成一个新的字符串(十六进制形式),输出该字符串并将其转换为十进制输出
  10. GPS术语 -- 词汇与概念解释(三)