Q_PLUGIN_METADATA Q_INTERFACE Q_DECLARE_INTERFACE qt_pluginMetaData

68 篇文章 11 订阅

使用版本:qt5.12.0

qt源码安装:qt creator debug无法调试 进入 qt源码_丘上人的博客-CSDN博客 

qtcreator对qt程序的编译过程是先执行qmake,然后构建(等价于make)。从项目->build->构建步骤可以看出,构建过程中先用qmake生成makefile,make过程用jom和前面产生的makefile生成目标程序的exe。jom按makefile规则的运行过程中根据依赖会先调用moc.exe生成moc_*.cpp文件,然后是按正常C++编译过程进行C++预编译(宏替换),再然后是C++编译。也就是说moc生成moc_*.cpp代码是在C++编译器进行预编译之前!jom具体如何调用moc的过程请参考:
qt 工程构建过程 默认构建路径设置 通过Dos窗口运行命令编译qt工程_丘上人的博客-CSDN博客_qt执行dos命令

总结:
Q_PLUGIN_METADATA、Q_INTERFACE、Q_DECLARE_INTERFACE三个标识符(Identifier)都具有C++宏和qt特有关键字的双重意义。

Q_DECALER_INTERFACE与Q_INTERFACE要成对使用,告诉qtmeta系统有关接口的情况,两者一起作用完善plugin在qt meta系统中的工作代码,简化的类型转换,使能够通过meta系统调用interface对象。(否则需要自己进行强制类型转换,在复杂工程中自己强制转换会比较乱且繁杂)

Q_PLUGIN_METADATA与Q_DECALER_INTERFACE和Q_INTERFACE两个宏基本没有关系,它主要是用于生成 插件相关工作的必要代码,使得后续QLibrary能进行相关插件的加载、卸载和函数调用,它才是生成插件的关键。

一、Q_PLUGIN_METADATA

Q_PLUGIN_METADATA与Q_OBJECT等等类似,其意义除了作为C++的宏定义之外,还是作为qt的特殊关键字,会被moc.exe进行特殊解释的关键字。也就是说Q_PLUGIN_METADATA是有双重意义的。moc.exe的本质是qt的源码半解释和源码生成工具。主要工作是词法分析,同时识别出代码中的qt特有的关键字,并根据代码中所含有的qt特有关键字生成moc_*.cpp代码。

通过QtInstallDir\Src\qtbase\src\tools\moc目录下的moc源码可知,moc\util\generate_keywords.cpp文件中有对Q_PLUGIN_METADATA的特殊解释,moc\下的generate_keywords.cpp与keyword.cpp、ppkeyword.cpp共同组成moc的简单的词法工具,辅助moc识别和标记代码中的特殊关键字,并初始化Moc对象中的symbols成员。

//yourprojectPath/CalPlugin.h  使用Q_PLUGIN_METADATA宏将类声明为qt插件。
class CalInterface
{
public:
    virtual ~CalInterface() {}
    virtual int add(int a,int b) = 0;
};


#define CalInterface_iid "Examples.Plugin.CalInterface"

QT_BEGIN_NAMESPACE
Q_DECLARE_INTERFACE(CalInterface,CalInterface_iid)
QT_END_NAMESPACE

class CalPlugin : public QObject,public CalInterface
{
    Q_OBJECT
    Q_INTERFACES(CalInterface)
    Q_PLUGIN_METADATA(IID CalInterface_iid FILE "calplugin.json")

public:
    explicit CalPlugin(QObject *parent = nullptr);
    int add(int a,int b);
};

 qt中 所有特有关键字与Token字符串组 如下:

//QtInstallDir\Qt5.12.0\5.12.0\Src\qtbase\src\tools\moc\generate_keyword.cpp
struct Keyword
{
    const char *lexem;
    const char *token;
};
static const Keyword keywords[] = {  //qt所有的特殊关键字对应的token
......    
    { "for", "FOR" },
    { "break", "BREAK" },
    { "continue", "CONTINUE" },
    { "goto", "GOTO" },
    { "return", "RETURN" },
    { "Q_OBJECT", "Q_OBJECT_TOKEN" },
    { "Q_NAMESPACE", "Q_NAMESPACE_TOKEN" },
    { "Q_GADGET", "Q_GADGET_TOKEN" },
    { "Q_PROPERTY", "Q_PROPERTY_TOKEN" },
    { "Q_PLUGIN_METADATA", "Q_PLUGIN_METADATA_TOKEN" },    ///Q_PLUGIN_METADATA
    { "Q_ENUMS", "Q_ENUMS_TOKEN" },
    { "Q_ENUM", "Q_ENUM_TOKEN" },
    { "Q_ENUM_NS", "Q_ENUM_NS_TOKEN" },
    { "Q_FLAGS", "Q_FLAGS_TOKEN" },
    { "Q_FLAG", "Q_FLAG_TOKEN" },
    { "Q_FLAG_NS", "Q_FLAG_NS_TOKEN" },
    { "Q_DECLARE_FLAGS", "Q_DECLARE_FLAGS_TOKEN" },
    { "Q_DECLARE_INTERFACE", "Q_DECLARE_INTERFACE_TOKEN" },
    { "Q_DECLARE_METATYPE", "Q_DECLARE_METATYPE_TOKEN" },
    { "Q_DECLARE_EXTENSION_INTERFACE", "Q_DECLARE_INTERFACE_TOKEN" },
    { "Q_SETS", "Q_FLAGS_TOKEN" },
    { "Q_CLASSINFO", "Q_CLASSINFO_TOKEN" },
    { "Q_INTERFACES", "Q_INTERFACES_TOKEN" },
    { "signals", "SIGNALS" },
    { "slots", "SLOTS" },
    { "Q_SIGNALS", "Q_SIGNALS_TOKEN" },
    { "Q_SLOTS", "Q_SLOTS_TOKEN" },
    { "Q_PRIVATE_SLOT", "Q_PRIVATE_SLOT_TOKEN" },
    { "QT_MOC_COMPAT", "Q_MOC_COMPAT_TOKEN" },
    { "Q_INVOKABLE", "Q_INVOKABLE_TOKEN" },
    { "Q_SIGNAL", "Q_SIGNAL_TOKEN" },
    { "Q_SLOT", "Q_SLOT_TOKEN" },
    { "Q_SCRIPTABLE", "Q_SCRIPTABLE_TOKEN" },
    { "Q_PRIVATE_PROPERTY", "Q_PRIVATE_PROPERTY_TOKEN" },
    { "Q_REVISION", "Q_REVISION_TOKEN" },
    { "\n", "NEWLINE" },
    { "\"", "QUOTE" },
......
}

从下面代码可以看出,Q_PLUGIN_METADATA作为C++的宏定义,并没有任何实际意义,QT_ANNOTATE_CLASS宏定义只是一个声明,完全被架空了,qt_plugin_metadata也就被架空了,啥也不是。

//QtInstallDir\Qt5.12.0\5.12.0\Src\qtbase\src\corelib\kernel\qobjectdefs.h
#ifndef QT_ANNOTATE_CLASS
# ifndef Q_COMPILER_VARIADIC_MACROS
#  define QT_ANNOTATE_CLASS(type, x)
# else
#  define QT_ANNOTATE_CLASS(type, ...)
# endif

.......

#define Q_PLUGIN_METADATA(x) QT_ANNOTATE_CLASS(qt_plugin_metadata, x)

qt程序编译过程是先执行qmake后构建,构建是会默认调用jom根据qmake产生的makefile规则进行执行,jom执行规则过程中根据规则依赖会调用moc生成moc_*.cpp文件,然后再是预编译(宏替换),然后再是编译。也就是说moc生成代码是在预编译之前!
Q_PLUGIN_METADATA作为QT的特有关键字,意义却很大。正确使用了该关键字会在moc_*.cpp下生成qt必须依赖的plugin相关的代码。


 moc.exe 的源码工程:路径为  QtInstallDir\Qt5.12.0\5.12.0\Src\qtbase\src\tools\moc

moc.exe 的源码  QtInstallDir\Src\qtbase\src\tools\moc 工程

 moc project中的main函数中的Preprocessor 对象的tokenize函数将输入的操作对象(此案例为CalPlugin.h文件)中所有的token都识别并标记出来,并将信息存放到Moc对象的的Symbols对象中。

//QtInstallDir\Qt5.12.0\5.12.0\Src\qtbase\src\tools\moc\preprocessor.cpp
Symbols Preprocessor::tokenize(const QByteArray& input, int lineNum, Preprocessor::TokenizeMode mode)
{//对moc中的symbols进行初始化的函数。
.....
}

在moc对象中通过Q_PLUGIN_METADATA及其后的IID 和FILE标识符初始化ClassDef对象def中的pluginData.iid。为后续生成moc_*.cpp做准备。

//QtInstallDir\Qt5.12.0\5.12.0\Src\qtbase\src\tools\moc\moc.cpp
void Moc::parse()
{
.....
                case Q_PLUGIN_METADATA_TOKEN://处理Q_PLUGIN_METADATA_TOKEN
                    parsePluginData(&def);
                    break;
.....
}


void Moc::parsePluginData(ClassDef *def)
{
    next(LPAREN);
    QByteArray metaData;
    while (test(IDENTIFIER)) {
        QByteArray l = lexem();
        if (l == "IID") {
            next(STRING_LITERAL);
            def->pluginData.iid = unquotedLexem();
        } else if (l == "FILE") {
            next(STRING_LITERAL);
            QByteArray metaDataFile = unquotedLexem();
            QFileInfo fi(QFileInfo(QString::fromLocal8Bit(currentFilenames.top().constData())).dir(), QString::fromLocal8Bit(metaDataFile.constData()));
            for (int j = 0; j < includes.size() && !fi.exists(); ++j) {
                const IncludePath &p = includes.at(j);
                if (p.isFrameworkPath)
                    continue;

                fi.setFile(QString::fromLocal8Bit(p.path.constData()), QString::fromLocal8Bit(metaDataFile.constData()));
                // try again, maybe there's a file later in the include paths with the same name
                if (fi.isDir()) {
                    fi = QFileInfo();
                    continue;
                }
            }
            if (!fi.exists()) {
                const QByteArray msg = "Plugin Metadata file " + lexem()
                        + " does not exist. Declaration will be ignored";
                error(msg.constData());
                return;
            }
            QFile file(fi.canonicalFilePath());
            if (!file.open(QFile::ReadOnly)) {
                QByteArray msg = "Plugin Metadata file " + lexem() + " could not be opened: "
                    + file.errorString().toUtf8();
                error(msg.constData());
                return;
            }
            metaData = file.readAll();
        }
    }

    if (!metaData.isEmpty()) {
        def->pluginData.metaData = QJsonDocument::fromJson(metaData);
        if (!def->pluginData.metaData.isObject()) {
            const QByteArray msg = "Plugin Metadata file " + lexem()
                    + " does not contain a valid JSON object. Declaration will be ignored";
            warning(msg.constData());
            def->pluginData.iid = QByteArray();
            return;
        }
    }

    mustIncludeQPluginH = true;
    next(RPAREN);
}

真正生成moc_*.cpp是在generator中,Moc对象将信息都收集好后便调用Generator对象的generateCode。

//QtInstallDir\Src\qtbase\src\tools\moc\generator.h
class Generator
{
    FILE *out;
    ClassDef *cdef;
    QVector<uint> meta_data;
public:
    Generator(ClassDef *classDef, const QVector<QByteArray> &metaTypes, const QHash<QByteArray, QByteArray> &knownQObjectClasses, const QHash<QByteArray, QByteArray> &knownGadgets, FILE *outfile = 0);
    void generateCode();
private:
    bool registerableMetaType(const QByteArray &propertyType);
    void registerClassInfoStrings();
    void generateClassInfos();
    void registerFunctionStrings(const QVector<FunctionDef> &list);
    void registerByteArrayVector(const QVector<QByteArray> &list);
    void generateFunctions(const QVector<FunctionDef> &list, const char *functype, int type, int &paramsIndex);
    void generateFunctionRevisions(const QVector<FunctionDef> &list, const char *functype);
    void generateFunctionParameters(const QVector<FunctionDef> &list, const char *functype);
    void generateTypeInfo(const QByteArray &typeName, bool allowEmptyName = false);
    void registerEnumStrings();
    void generateEnums(int index);
    void registerPropertyStrings();
    void generateProperties();
    void generateMetacall();
    void generateStaticMetacall();
    void generateSignal(FunctionDef *def, int index);
    void generatePluginMetaData();
......
}

------------------------------------------------------
//QtInstallDir\Src\qtbase\src\tools\moc\generator.cpp
void Generator::generateCode()
{
..........
//
// Build classinfo array
//
    generateClassInfos();

//
// Build signals array first, otherwise the signal indices would be wrong
//
    generateFunctions(cdef->signalList, "signal", MethodSignal, paramsIndex);

//
// Build slots array
//
    generateFunctions(cdef->slotList, "slot", MethodSlot, paramsIndex);

//
// Build method array
//
    generateFunctions(cdef->methodList, "method", MethodMethod, paramsIndex);

//
// Build method version arrays
//
    if (cdef->revisionedMethods) {
        generateFunctionRevisions(cdef->signalList, "signal");
        generateFunctionRevisions(cdef->slotList, "slot");
        generateFunctionRevisions(cdef->methodList, "method");
    }

//
// Build method parameters array
//
    generateFunctionParameters(cdef->signalList, "signal");
    generateFunctionParameters(cdef->slotList, "slot");
    generateFunctionParameters(cdef->methodList, "method");
    if (isConstructible)
        generateFunctionParameters(cdef->constructorList, "constructor");

//
// Build property array
//
    generateProperties();

//
// Build enums array
//
    generateEnums(enumsIndex);

//
// Build constructors array
//
    if (isConstructible)
        generateFunctions(cdef->constructorList, "constructor", MethodConstructor, paramsIndex);

//
// Terminate data array
//
    fprintf(out, "\n       0        // eod\n};\n\n");

//
// Generate internal qt_static_metacall() function
//
    const bool hasStaticMetaCall = !isQt &&
            (cdef->hasQObject || !cdef->methodList.isEmpty()
             || !cdef->propertyList.isEmpty() || !cdef->constructorList.isEmpty());
    if (hasStaticMetaCall)
        generateStaticMetacall();

//
// Build extra array
//
.......
}

void Generator::generatePluginMetaData()
{
    if (cdef->pluginData.iid.isEmpty())
        return;

    fputs("\nQT_PLUGIN_METADATA_SECTION\n"
          "static constexpr unsigned char qt_pluginMetaData[] = {\n"
          "    'Q', 'T', 'M', 'E', 'T', 'A', 'D', 'A', 'T', 'A', ' ', '!',\n"
          "    // metadata version, Qt version, architectural requirements\n"
          "    0, QT_VERSION_MAJOR, QT_VERSION_MINOR, qPluginArchRequirements(),", out);


    CborDevice dev(out);
    CborEncoder enc;
    cbor_encoder_init_writer(&enc, CborDevice::callback, &dev);

    CborEncoder map;
    cbor_encoder_create_map(&enc, &map, CborIndefiniteLength);

    dev.nextItem("\"IID\"");
    cbor_encode_int(&map, int(QtPluginMetaDataKeys::IID));
    cbor_encode_text_string(&map, cdef->pluginData.iid.constData(), cdef->pluginData.iid.size());

    dev.nextItem("\"className\"");
    cbor_encode_int(&map, int(QtPluginMetaDataKeys::ClassName));
    cbor_encode_text_string(&map, cdef->classname.constData(), cdef->classname.size());

    QJsonObject o = cdef->pluginData.metaData.object();
    if (!o.isEmpty()) {
        dev.nextItem("\"MetaData\"");
        cbor_encode_int(&map, int(QtPluginMetaDataKeys::MetaData));
        jsonObjectToCbor(&map, o);
    }

    // Add -M args from the command line:
    for (auto it = cdef->pluginData.metaArgs.cbegin(), end = cdef->pluginData.metaArgs.cend(); it != end; ++it) {
        const QJsonArray &a = it.value();
        QByteArray key = it.key().toUtf8();
        dev.nextItem(QByteArray("command-line \"" + key + "\"").constData());
        cbor_encode_text_string(&map, key.constData(), key.size());
        jsonArrayToCbor(&map, a);
    }

    // Close the CBOR map manually
    dev.nextItem();
    cbor_encoder_close_container(&enc, &map);
    fputs("\n};\n", out);

    // 'Use' all namespaces.
    int pos = cdef->qualified.indexOf("::");
    for ( ; pos != -1 ; pos = cdef->qualified.indexOf("::", pos + 2) )
        fprintf(out, "using namespace %s;\n", cdef->qualified.left(pos).constData());
    fprintf(out, "QT_MOC_EXPORT_PLUGIN(%s, %s)\n\n",
            cdef->qualified.constData(), cdef->classname.constData());
}

void Generator::generateMetacall()
{
    bool isQObject = (cdef->classname == "QObject");

    fprintf(out, "\nint %s::qt_metacall(QMetaObject::Call _c, int _id, void **_a)\n{\n",
             cdef->qualified.constData());

    if (!purestSuperClass.isEmpty() && !isQObject) {
        QByteArray superClass = purestSuperClass;
        fprintf(out, "    _id = %s::qt_metacall(_c, _id, _a);\n", superClass.constData());
    }


    bool needElse = false;
    QVector<FunctionDef> methodList;
    methodList += cdef->signalList;
    methodList += cdef->slotList;
    methodList += cdef->methodList;

    // If there are no methods or properties, we will return _id anyway, so
    // don't emit this comparison -- it is unnecessary, and it makes coverity
    // unhappy.
    if (methodList.size() || cdef->propertyList.size()) {
        fprintf(out, "    if (_id < 0)\n        return _id;\n");
    }
...........
}

最后生成moc_*.cpp代码中plugin必须依赖的相关代码包括qt_pluginMetaData数组变量(注意这QT_ANNOTATE_CLASS宏定义中的qt_plugin_metadata没有任何关系)和QT_MOC_EXPORT_PLUGIN宏。 

//youProjectPath\moc_CalPlugin.cpp
//moc 识别到原有代码中的有效的Q_PLUGIN_METADATA标识后,在moc_*.cpp中生成的相关代码

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 requirements
    0, QT_VERSION_MAJOR, QT_VERSION_MINOR, qPluginArchRequirements(),
    0xbf, 
    // "IID"
    0x02,  0x78,  0x1c,  'E',  'x',  'a',  'm',  'p', 
    'l',  'e',  's',  '.',  'P',  'l',  'u',  'g', 
    'i',  'n',  '.',  'C',  'a',  'l',  'I',  'n', 
    't',  'e',  'r',  'f',  'a',  'c',  'e', 
    // "className"
    0x03,  0x69,  'C',  'a',  'l',  'P',  'l',  'u', 
    'g',  'i',  'n', 
    0xff, 
};
QT_MOC_EXPORT_PLUGIN(CalPlugin, CalPlugin)
//QtInstallDir\Src\qtbase\src\corelib\plugin\qplugin.h

#define Q_IMPORT_PLUGIN(PLUGIN) \  //与静态库配套使用的宏,该宏使用可以参考plugandpaint example中main.cpp
        extern const QT_PREPEND_NAMESPACE(QStaticPlugin) qt_static_plugin_##PLUGIN(); \
        class Static##PLUGIN##PluginInstance{ \
        public: \
                Static##PLUGIN##PluginInstance() { \
                    qRegisterStaticPluginFunction(qt_static_plugin_##PLUGIN()); \
                } \
        }; \
       static Static##PLUGIN##PluginInstance static##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; \
        }

#if defined(QT_STATICPLUGIN)  //QT_MOC_EXPORT_PLUGIN宏根据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)

#endif

QT_MOC_EXPORT_PLUGIN中定义的qt_plugin_instance函数在QLibraryPrivate对象中的loadPlugin中被调用。

//QtInstallDir\Src\qtbase\src\corelib\plugin\qlibrary.cpp
bool QLibraryPrivate::loadPlugin()
{
    if (instance) {
        libraryUnloadCount.ref();
        return true;
    }
    if (pluginState == IsNotAPlugin)
        return false;
    if (load()) {
        instance = (QtPluginInstanceFunction)resolve("qt_plugin_instance");
        return instance;
    }
    if (qt_debug_component())
        qWarning() << "QLibraryPrivate::loadPlugin failed on" << fileName << ":" << errorString;
    pluginState = IsNotAPlugin;
    return false;
}

QFunctionPointer QLibraryPrivate::resolve(const char *symbol)
{
    if (!pHnd)
        return 0;
    return resolve_sys(symbol);
}

-----------------------------------
//QtInstallDir\Src\qtbase\src\corelib\plugin\qlibrary_win.cpp

QFunctionPointer QLibraryPrivate::resolve_sys(const char* symbol)
{
    FARPROC address = GetProcAddress(pHnd, symbol);
    if (!address) {
        errorString = QLibrary::tr("Cannot resolve symbol \"%1\" in %2: %3").arg(
            QString::fromLatin1(symbol), QDir::toNativeSeparators(fileName), qt_error_string());
    } else {
        errorString.clear();
    }
    return QFunctionPointer(address);
}

二、Q_INTERFACE

Q_INTERFACE宏也具有两重意义,作为C++宏,是没有意义的。

//QtInstallDir\Qt5.12.0\5.12.0\msvc2015_64\include\QtCore\qobjectdefs.h
......
#ifndef QT_ANNOTATE_CLASS
# ifndef Q_COMPILER_VARIADIC_MACROS
#  define QT_ANNOTATE_CLASS(type, x)
# else
#  define QT_ANNOTATE_CLASS(type, ...)
# endif
#endif
........
#define Q_INTERFACES(x) QT_ANNOTATE_CLASS(qt_interfaces, x)
......

作为qt 特有关键字,moc识别到之后会将Interface信息(包括类名及类IID,类IID从解析Q_DECLARE_INTERFACE后的interface2IdMap中获取)加入到InterfaceList的链表中,为后续generator生成相关代码做准备。

//QtInstallDir\Src\qtbase\src\tools\moc\moc.h
......
struct Interface
    {
        Interface() {} // for QVector, don't use
        inline explicit Interface(const QByteArray &_className)
            : className(_className) {}
        QByteArray className;
        QByteArray interfaceId;
    };
    QVector<QVector<Interface> >interfaceList;
......

------------------------
//QtInstallDir\Src\qtbase\src\tools\moc\moc.cpp
void Moc::parse()
{
.......
                case Q_INTERFACES_TOKEN:
                    parseInterfaces(&def);
                    break;
.......
}

void Moc::parseInterfaces(ClassDef *def)
{
    next(LPAREN);
    while (test(IDENTIFIER)) {   //interface可以一次声明多个Q_INTERFACES(BrushInterface ShapeInterface FilterInterface)
        QVector<ClassDef::Interface> iface;
        iface += ClassDef::Interface(lexem());
        while (test(SCOPE)) {
            iface.last().className += lexem();
            next(IDENTIFIER);
            iface.last().className += lexem();
        }
        while (test(COLON)) {
            next(IDENTIFIER);
            iface += ClassDef::Interface(lexem());
            while (test(SCOPE)) {
                iface.last().className += lexem();
                next(IDENTIFIER);
                iface.last().className += lexem();
            }
        }
        // resolve from classnames to interface ids
        for (int i = 0; i < iface.count(); ++i) {
            const QByteArray iid = interface2IdMap.value(iface.at(i).className);
            if (iid.isEmpty())
                error("Undefined interface");

            iface[i].interfaceId = iid;
        }
        def->interfaceList += iface;
    }
    next(RPAREN);
}

 generator中根据interfacelist链表生成moc_*.cpp中qt_metacast(const char *_clname)函数内容。

//QtInstallDir\Src\qtbase\src\tools\moc\generator.cpp
void Generator::generateCode(){
.......
    fprintf(out, "\nvoid *%s::qt_metacast(const char *_clname)\n{\n", cdef->qualified.constData());
    fprintf(out, "    if (!_clname) return nullptr;\n");
    fprintf(out, "    if (!strcmp(_clname, qt_meta_stringdata_%s.stringdata0))\n"
                  "        return static_cast<void*>(this);\n",
            qualifiedClassNameIdentifier.constData());
    for (int i = 1; i < cdef->superclassList.size(); ++i) { // for all superclasses but the first one
        if (cdef->superclassList.at(i).second == FunctionDef::Private)
            continue;
        const char *cname = cdef->superclassList.at(i).first.constData();
        fprintf(out, "    if (!strcmp(_clname, \"%s\"))\n        return static_cast< %s*>(this);\n",
                cname, cname);
    }
    for (int i = 0; i < cdef->interfaceList.size(); ++i) {
        const QVector<ClassDef::Interface> &iface = cdef->interfaceList.at(i);
        for (int j = 0; j < iface.size(); ++j) {
            fprintf(out, "    if (!strcmp(_clname, %s))\n        return ", iface.at(j).interfaceId.constData());
            for (int k = j; k >= 0; --k)
                fprintf(out, "static_cast< %s*>(", iface.at(k).className.constData());
            fprintf(out, "this%s;\n", QByteArray(j + 1, ')').constData());
        }
    }
    if (!purestSuperClass.isEmpty() && !isQObject) {
        QByteArray superClass = purestSuperClass;
        fprintf(out, "    return %s::qt_metacast(_clname);\n", superClass.constData());
    } else {
        fprintf(out, "    return nullptr;\n");
    }
    fprintf(out, "}\n");
......
}

 下面是qt examples 中的plugandpaint插件相关部分代码

​//QtInstalldir/Qt5.12.0\Examples\Qt-5.12.0\widgets\tools\plugandpaint\plugins\basictools\basictoolsplugin.h
......
Q_INTERFACES(BrushInterface ShapeInterface FilterInterface)  //一次性同时声明多个interface
......
//QtInstalldir/Qt5.12.0\Examples\Qt-5.12.0\widgets\tools\plugandpaint\plugins\basictools\moc_basictoolsplugin.cpp
void *BasicToolsPlugin::qt_metacast(const char *_clname)
{
    if (!_clname) return nullptr;
    if (!strcmp(_clname, qt_meta_stringdata_BasicToolsPlugin.stringdata0))
        return static_cast<void*>(this);
    if (!strcmp(_clname, "BrushInterface"))
        return static_cast< BrushInterface*>(this);
    if (!strcmp(_clname, "ShapeInterface"))
        return static_cast< ShapeInterface*>(this);
    if (!strcmp(_clname, "FilterInterface"))
        return static_cast< FilterInterface*>(this);
    if (!strcmp(_clname, "org.qt-project.Qt.Examples.PlugAndPaint.BrushInterface/1.0"))
        return static_cast< BrushInterface*>(this);
    if (!strcmp(_clname, "org.qt-project.Qt.Examples.PlugAndPaint.ShapeInterface/1.0"))
        return static_cast< ShapeInterface*>(this);
    if (!strcmp(_clname, "org.qt-project.Qt.Examples.PlugAndPaint.FilterInterface/1.0"))
        return static_cast< FilterInterface*>(this);
    return QObject::qt_metacast(_clname);
}

.......
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 requirements
    0, QT_VERSION_MAJOR, QT_VERSION_MINOR, qPluginArchRequirements(),
    0xbf, 
    // "IID"
    0x02,  0x78,  0x36,  'o',  'r',  'g',  '.',  'q', 
    't',  '-',  'p',  'r',  'o',  'j',  'e',  'c', 
    't',  '.',  'Q',  't',  '.',  'E',  'x',  'a', 
    'm',  'p',  'l',  'e',  's',  '.',  'P',  'l', 
    'u',  'g',  'A',  'n',  'd',  'P',  'a',  'i', 
    'n',  't',  '.',  'B',  'r',  'u',  's',  'h', 
    'I',  'n',  't',  'e',  'r',  'f',  'a',  'c', 
    'e', 
    // "className"
    0x03,  0x70,  'B',  'a',  's',  'i',  'c',  'T', 
    'o',  'o',  'l',  's',  'P',  'l',  'u',  'g', 
    'i',  'n', 
    0xff, 
};
QT_MOC_EXPORT_PLUGIN(BasicToolsPlugin, BasicToolsPlugin)

三、Q_DECLARE_INTERFACE

Q_DECLARE_INTERFACE也有两重意义,作为C++宏,是对模板函数qobject_cast(...)、const qobject_cast(...)、qobject_interface_iid(...)的特化;作为qt特有关键字,用于作为 (key,value)对 初始化moc中关于interface 的map(moc对象中的interface2IdMap对象成员),为后续解析Q_INTERFACE做准备,具体代码就不展示了,有兴趣可以查看“void Moc::parseDeclareInterface()”。

To make it possible to query at run-time whether a plugin implements a given interface, we must use the Q_DECLARE_INTERFACE() macro.The first argument is the name of the interface. The second argument is a string identifying the interface in a unique way. By convention, we use a "Java package name" syntax to identify interfaces. If we later change the interfaces, we must use a different string to identify the new interface; otherwise, the application might crash. It is therefore a good idea to include a version number in the string, as we did above.

QtInstallDir\Src\qtbase\src\corelib\kernel\qobject.h
......
template <class T>
inline T qobject_cast(QObject *object)
{
    typedef typename std::remove_cv<typename std::remove_pointer<T>::type>::type ObjType;
    Q_STATIC_ASSERT_X(QtPrivate::HasQ_OBJECT_Macro<ObjType>::Value,
                    "qobject_cast requires the type to have a Q_OBJECT macro");
    return static_cast<T>(ObjType::staticMetaObject.cast(object));
}

template <class T>
inline T qobject_cast(const QObject *object)
{
    typedef typename std::remove_cv<typename std::remove_pointer<T>::type>::type ObjType;
    Q_STATIC_ASSERT_X(QtPrivate::HasQ_OBJECT_Macro<ObjType>::Value,
                      "qobject_cast requires the type to have a Q_OBJECT macro");
    return static_cast<T>(ObjType::staticMetaObject.cast(object));
}


template <class T> inline const char * qobject_interface_iid()
{ return nullptr; }

#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

四、qt_pluginMetaData与json文件

qt自定义插件需要将IID与json文件绑定,json文件内容可以为空{}。其目的是为了方便将标识或参数等内容输入到dll中。

Q_PLUGIN_METADATA(IID CalInterface_iid FILE "calplugin.json")

qt_pluginMetaData[] 中从IID项开始是CBor格式,qt中有CBorStreamReader CBorStreamWriter TynyCBor对CBor格式内容进行操作。moc 生成qt_pluginMetaData[] 时可以将json文件中的内容和在pro文件中传递给moc参数是通过 -M指定的内容放入其中
比如calplugin.json内容如下:

{
    "FirstName": "John",
    "LastName": "Doe",
    "Age": 43,
    "Address": {
        "Street": "Downing Street 10",
        "City": "London",
        "Country": "Great Britain"
    },
    "Phone numbers": [
        "+44 1234567",
        "+44 2345678"
    ]
}

pro文件传递参数给moc如下:
QMAKE_MOC_OPTIONS = "-M yy=ttet"
 

通过pro文件对moc传递参数
pro文件设置moc参数后会写入到生成的makefile.debug中

设置calplugin.json和QMAKE_MOC_OPTIONS参数后最终生成的qt_pluginMetaData[]会变成如下:

//moc_*.cpp
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 requirements
    0, QT_VERSION_MAJOR, QT_VERSION_MINOR, qPluginArchRequirements(),
    0xbf, 
    // "IID"
    0x02,  0x78,  0x3b,  'o',  'r',  'g',  '.',  'q', 
    't',  '-',  'p',  'r',  'o',  'j',  'e',  'c', 
    't',  '.',  'Q',  't',  '.',  'E',  'x',  'a', 
    'm',  'p',  'l',  'e',  's',  '.',  'P',  'l', 
    'u',  'g',  'A',  'n',  'd',  'P',  'a',  'i', 
    'n',  't',  '.',  'F',  'i',  'l',  't',  'e', 
    'r',  'I',  'n',  't',  'e',  'r',  'f',  'a', 
    'c',  'e',  '/',  '1',  '.',  '0', 
    // "className"
    0x03,  0x72,  'E',  'x',  't',  'r',  'a',  'F', 
    'i',  'l',  't',  'e',  'r',  's',  'P',  'l', 
    'u',  'g',  'i',  'n', 
    // "MetaData"
    0x04,  0xa5,  0x67,  'A',  'd',  'd',  'r',  'e', 
    's',  's',  0xa3,  0x64,  'C',  'i',  't',  'y', 
    0x66,  'L',  'o',  'n',  'd',  'o',  'n',  0x67, 
    'C',  'o',  'u',  'n',  't',  'r',  'y',  0x6d, 
    'G',  'r',  'e',  'a',  't',  ' ',  'B',  'r', 
    'i',  't',  'a',  'i',  'n',  0x66,  'S',  't', 
    'r',  'e',  'e',  't',  0x71,  'D',  'o',  'w', 
    'n',  'i',  'n',  'g',  ' ',  'S',  't',  'r', 
    'e',  'e',  't',  ' ',  '1',  '0',  0x63,  'A', 
    'g',  'e',  0x18,  0x2b,  0x69,  'F',  'i',  'r', 
    's',  't',  'N',  'a',  'm',  'e',  0x64,  'J', 
    'o',  'h',  'n',  0x68,  'L',  'a',  's',  't', 
    'N',  'a',  'm',  'e',  0x63,  'D',  'o',  'e', 
    0x6d,  'P',  'h',  'o',  'n',  'e',  ' ',  'n', 
    'u',  'm',  'b',  'e',  'r',  's',  0x82,  0x6b, 
    '+',  '4',  '4',  ' ',  '1',  '2',  '3',  '4', 
    '5',  '6',  '7',  0x6b,  '+',  '4',  '4',  ' ', 
    '2',  '3',  '4',  '5',  '6',  '7',  '8', 
    // command-line "yy"
    0x62,  'y',  'y',  0x81,  0x64,  't',  't',  'e', 
    't', 
    0xff, 
};
QT_MOC_EXPORT_PLUGIN(CalPlugin, CalPlugin)

C++动态加载动态库 调用库中类 及类中方法_丘上人的博客-CSDN博客_c++动态加载动态库 
qt example plugandpaint 插件 动态库 pnp_extrafiltersd.dll无法加载问题_丘上人的博客-CSDN博客

  • 1
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值