摘要
分析Q_PLUGIN_METADATA
宏的设计意图,站在设计者的意图进行插件的高屋建瓴式学习。
Meta-Object Compiler 简称MOC
Qt 的 Meta-Object Compiler(MOC)是一个预处理器,用于处理带有特殊关键字的 C ++ 文件,并生成用于 Qt 的元对象代码。这是因为 Qt 提供了信号和槽机制,允许对象之间的通信。为了实现这一点,Qt 通过在每个QObject衍生类上创建一个“元对象”来动态识别其成员函数和其它属性,例如它们是否是信号或槽。
MOC 是 Qt 的一部分,在安装 Qt 的时候会安装。在 Qt 的开发中,当 .h 文件中的 QObject 衍生类含有 Q_OBJECT 宏时,必须通过 MOC 来进行预处理,生成额外的元数据。
MOC 的工作方式是扫描包含 Q_OBJECT 宏的 .h 文件,创建元数据并生成对应的 C++ 代码。此后,开发人员将使用 C++ 代码连接此元数据,以便在代码中使用信号和槽的机制。
Q_PLUGIN_METADATA宏的作用
Qt插件是把一组功能进行封装,以静态库和动态库的形式提供给程序调用。在学习Qt之前,我已经知道了C++动态库就是把功能封装,通过导出函数的方式供调用者调用。C++动态库是通过dllexport关键字,告诉编译器把函数或类进行导出,动态库只有被导出的函数才能被调用方(外部)找到。
Qt插件是在C++动态库基础之上进行的扩展,支持了信号-槽等新特性。Qt插件也需要告诉MOC
导出了哪些函数、类。Qt中通过宏Q_PLUGIN_METADATA告诉MOC,哪个类被导出,也就是哪个类供外部调用。
*.h *.cpp文件、Q_PLUGIN_METADATA宏、MOC的关系图
上代码
分析这个Q_PLUGIN_METADATA
宏会使得MOC生成了哪些代码。先看下面的头文件。IID
是给插件导处的类起个唯一的名字(字符串的形式),底层是个GUID。FILE
是给插件属性信息指定个配置文件,暂时没用到先不管。
#ifndef ACCESSIBLEPLUGIN_H
#define ACCESSIBLEPLUGIN_H
#include <QAccessiblePlugin>
class AccessiblePlugin : public QAccessiblePlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QAccessibleFactoryInterface" FILE "simplePlugin.json")
public:
explicit AccessiblePlugin(QObject *parent = nullptr);
private:
QAccessibleInterface *create(const QString &key, QObject *object) override;
};
#endif // ACCESSIBLEPLUGIN_H
我们看下编译后生成了些啥😱。如下是MOC编译出的 moc_accessibleplugin.cpp 文件。
/****************************************************************************
** Meta object code from reading C++ file 'accessibleplugin.h'
**
** Created by: The Qt Meta Object Compiler version 67 (Qt 5.9.9)
**
** WARNING! All changes made in this file will be lost!
*****************************************************************************/
#include "../../simplePlugin/accessibleplugin.h"
#include <QtCore/qbytearray.h>
#include <QtCore/qmetatype.h>
#include <QtCore/qplugin.h>
#if !defined(Q_MOC_OUTPUT_REVISION)
#error "The header file 'accessibleplugin.h' doesn't include <QObject>."
#elif Q_MOC_OUTPUT_REVISION != 67
#error "This file was generated using the moc from 5.9.9. It"
#error "cannot be used with the include files from this version of Qt."
#error "(The moc has changed too much.)"
#endif
QT_BEGIN_MOC_NAMESPACE
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
struct qt_meta_stringdata_AccessiblePlugin_t {
QByteArrayData data[1];
char stringdata0[17];
};
#define QT_MOC_LITERAL(idx, ofs, len) \
Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \
qptrdiff(offsetof(qt_meta_stringdata_AccessiblePlugin_t, stringdata0) + ofs \
- idx * sizeof(QByteArrayData)) \
)
static const qt_meta_stringdata_AccessiblePlugin_t qt_meta_stringdata_AccessiblePlugin = {
{
QT_MOC_LITERAL(0, 0, 16) // "AccessiblePlugin"
},
"AccessiblePlugin"
};
#undef QT_MOC_LITERAL
static const uint qt_meta_data_AccessiblePlugin[] = {
// content:
7, // revision
0, // classname
0, 0, // classinfo
0, 0, // methods
0, 0, // properties
0, 0, // enums/sets
0, 0, // constructors
0, // flags
0, // signalCount
0 // eod
};
void AccessiblePlugin::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
Q_UNUSED(_o);
Q_UNUSED(_id);
Q_UNUSED(_c);
Q_UNUSED(_a);
}
const QMetaObject AccessiblePlugin::staticMetaObject = {
{ &QAccessiblePlugin::staticMetaObject, qt_meta_stringdata_AccessiblePlugin.data,
qt_meta_data_AccessiblePlugin, qt_static_metacall, nullptr, nullptr}
};
const QMetaObject *AccessiblePlugin::metaObject() const
{
return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject;
}
void *AccessiblePlugin::qt_metacast(const char *_clname)
{
if (!_clname) return nullptr;
if (!strcmp(_clname, qt_meta_stringdata_AccessiblePlugin.stringdata0))
return static_cast<void*>(this);
return QAccessiblePlugin::qt_metacast(_clname);
}
int AccessiblePlugin::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
{
_id = QAccessiblePlugin::qt_metacall(_c, _id, _a);
return _id;
}
QT_PLUGIN_METADATA_SECTION const uint qt_section_alignment_dummy = 42;
#ifdef QT_NO_DEBUG
QT_PLUGIN_METADATA_SECTION
static const unsigned char qt_pluginMetaData[] = {
'Q', 'T', 'M', 'E', 'T', 'A', 'D', 'A', 'T', 'A', ' ', ' ',
'q', 'b', 'j', 's', 0x01, 0x00, 0x00, 0x00,
0xd4, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
0xc0, 0x00, 0x00, 0x00, 0x1b, 0x03, 0x00, 0x00,
0x03, 0x00, 'I', 'I', 'D', 0x00, 0x00, 0x00,
'-', 0x00, 'o', 'r', 'g', '.', 'q', 't',
'-', 'p', 'r', 'o', 'j', 'e', 'c', 't',
'.', 'Q', 't', '.', 'Q', 'A', 'c', 'c',
'e', 's', 's', 'i', 'b', 'l', 'e', 'F',
'a', 'c', 't', 'o', 'r', 'y', 'I', 'n',
't', 'e', 'r', 'f', 'a', 'c', 'e', 0x00,
0x1b, 0x0b, 0x00, 0x00, 0x09, 0x00, 'c', 'l',
'a', 's', 's', 'N', 'a', 'm', 'e', 0x00,
0x10, 0x00, 'A', 'c', 'c', 'e', 's', 's',
'i', 'b', 'l', 'e', 'P', 'l', 'u', 'g',
'i', 'n', 0x00, 0x00, ':', '!', 0xa1, 0x00,
0x07, 0x00, 'v', 'e', 'r', 's', 'i', 'o',
'n', 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00,
0x05, 0x00, 'd', 'e', 'b', 'u', 'g', 0x00,
0x15, 0x13, 0x00, 0x00, 0x08, 0x00, 'M', 'e',
't', 'a', 'D', 'a', 't', 'a', 0x00, 0x00,
'(', 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
'$', 0x00, 0x00, 0x00, 0x14, 0x03, 0x00, 0x00,
0x04, 0x00, 'K', 'e', 'y', 's', 0x00, 0x00,
0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
0x0c, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00,
'H', 0x00, 0x00, 0x00, '|', 0x00, 0x00, 0x00,
'l', 0x00, 0x00, 0x00
};
#else // QT_NO_DEBUG
QT_PLUGIN_METADATA_SECTION
static const unsigned char qt_pluginMetaData[] = {
'Q', 'T', 'M', 'E', 'T', 'A', 'D', 'A', 'T', 'A', ' ', ' ',
'q', 'b', 'j', 's', 0x01, 0x00, 0x00, 0x00,
0xd4, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
0xc0, 0x00, 0x00, 0x00, 0x1b, 0x03, 0x00, 0x00,
0x03, 0x00, 'I', 'I', 'D', 0x00, 0x00, 0x00,
'-', 0x00, 'o', 'r', 'g', '.', 'q', 't',
'-', 'p', 'r', 'o', 'j', 'e', 'c', 't',
'.', 'Q', 't', '.', 'Q', 'A', 'c', 'c',
'e', 's', 's', 'i', 'b', 'l', 'e', 'F',
'a', 'c', 't', 'o', 'r', 'y', 'I', 'n',
't', 'e', 'r', 'f', 'a', 'c', 'e', 0x00,
0x15, 0x0b, 0x00, 0x00, 0x08, 0x00, 'M', 'e',
't', 'a', 'D', 'a', 't', 'a', 0x00, 0x00,
'(', 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
'$', 0x00, 0x00, 0x00, 0x14, 0x03, 0x00, 0x00,
0x04, 0x00, 'K', 'e', 'y', 's', 0x00, 0x00,
0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
0x1b, 0x12, 0x00, 0x00, 0x09, 0x00, 'c', 'l',
'a', 's', 's', 'N', 'a', 'm', 'e', 0x00,
0x10, 0x00, 'A', 'c', 'c', 'e', 's', 's',
'i', 'b', 'l', 'e', 'P', 'l', 'u', 'g',
'i', 'n', 0x00, 0x00, '1', 0x00, 0x00, 0x00,
0x05, 0x00, 'd', 'e', 'b', 'u', 'g', 0x00,
':', '!', 0xa1, 0x00, 0x07, 0x00, 'v', 'e',
'r', 's', 'i', 'o', 'n', 0x00, 0x00, 0x00,
0x0c, 0x00, 0x00, 0x00, 'H', 0x00, 0x00, 0x00,
0x80, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00,
0xb0, 0x00, 0x00, 0x00
};
#endif // QT_NO_DEBUG
QT_MOC_EXPORT_PLUGIN(AccessiblePlugin, AccessiblePlugin)
QT_WARNING_POP
QT_END_MOC_NAMESPACE
🍖最大的块头。static const unsigned char qt_pluginMetaData[] = {};
这是一个字符串数组,里面存储的插件的信息。整理下排版如下:
//非Debug版
QTMETADATA qbjs1 d4 b c0 1b3 3
IID - org.qt-project.Qt.QAccessibleFactoryInterface 1bb 9
className 10 AccessiblePlugin :!a1 7
version 11 5
debug 1513 8
MetaData ( 3 $ 143 4 Keys c c c 88 H | l
//Debug版
QTMETADATA qbjs1 d4 b c0 1b3 3
IID - org.qt-project.Qt.QAccessibleFactoryInterface 15b 8
MetaData **********
className 10 AccessiblePlugin 31 5
debug :!a1 7
version c H 80 a4 b0
这两个版本都有IID信息和className。IID字符串和宏里面定义的字符串是一样的,记住IID是导出类的身份ID。className 就是我们插件导出的类名。
回到Q_PLUGIN_METADATA宏的作用
上文分析了宏对moc文件的影响,最明显的是这个宏影响了moc文件的字符数组qt_pluginMetaData
的值。Qt的MOC通过这个字符数组知道插件的导出类名、IID的值等信息。正如我们通过dllexport函数告诉C++编译器,要导出某个类一样,只不过这个宏是告诉Qt的MOC要导出的类。
// 进一步的分析需要对MOC、 META-DATA、 QObject、 d指针有深入的剖析。