【Qt】插件Plugin入门之Q_PLUGIN_METADATA()宏【2023.05.07】

  • Post author:
  • Post category:其他



第一篇 插件Plugin入门之Q_PLUGIN_METADATA()宏


第二篇 插件Plugin入门之Q_PLUGIN_METADATA、Q_INTERFACE、Q_DECLARE_INTERFACE的功能剖析

第三篇:插件Plugin简明使用教程,未完




摘要

分析

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指针有深入的剖析。



版权声明:本文为qq_25231249原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。