• Q_PLUGIN_METADATA


    使用版本: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成员。

    1. //yourprojectPath/CalPlugin.h 使用Q_PLUGIN_METADATA宏将类声明为qt插件。
    2. class CalInterface
    3. {
    4. public:
    5. virtual ~CalInterface() {}
    6. virtual int add(int a,int b) = 0;
    7. };
    8. #define CalInterface_iid "Examples.Plugin.CalInterface"
    9. QT_BEGIN_NAMESPACE
    10. Q_DECLARE_INTERFACE(CalInterface,CalInterface_iid)
    11. QT_END_NAMESPACE
    12. class CalPlugin : public QObject,public CalInterface
    13. {
    14. Q_OBJECT
    15. Q_INTERFACES(CalInterface)
    16. Q_PLUGIN_METADATA(IID CalInterface_iid FILE "calplugin.json")
    17. public:
    18. explicit CalPlugin(QObject *parent = nullptr);
    19. int add(int a,int b);
    20. };

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

    1. //QtInstallDir\Qt5.12.0\5.12.0\Src\qtbase\src\tools\moc\generate_keyword.cpp
    2. struct Keyword
    3. {
    4. const char *lexem;
    5. const char *token;
    6. };
    7. static const Keyword keywords[] = { //qt所有的特殊关键字对应的token
    8. ......
    9. { "for", "FOR" },
    10. { "break", "BREAK" },
    11. { "continue", "CONTINUE" },
    12. { "goto", "GOTO" },
    13. { "return", "RETURN" },
    14. { "Q_OBJECT", "Q_OBJECT_TOKEN" },
    15. { "Q_NAMESPACE", "Q_NAMESPACE_TOKEN" },
    16. { "Q_GADGET", "Q_GADGET_TOKEN" },
    17. { "Q_PROPERTY", "Q_PROPERTY_TOKEN" },
    18. { "Q_PLUGIN_METADATA", "Q_PLUGIN_METADATA_TOKEN" }, ///Q_PLUGIN_METADATA
    19. { "Q_ENUMS", "Q_ENUMS_TOKEN" },
    20. { "Q_ENUM", "Q_ENUM_TOKEN" },
    21. { "Q_ENUM_NS", "Q_ENUM_NS_TOKEN" },
    22. { "Q_FLAGS", "Q_FLAGS_TOKEN" },
    23. { "Q_FLAG", "Q_FLAG_TOKEN" },
    24. { "Q_FLAG_NS", "Q_FLAG_NS_TOKEN" },
    25. { "Q_DECLARE_FLAGS", "Q_DECLARE_FLAGS_TOKEN" },
    26. { "Q_DECLARE_INTERFACE", "Q_DECLARE_INTERFACE_TOKEN" },
    27. { "Q_DECLARE_METATYPE", "Q_DECLARE_METATYPE_TOKEN" },
    28. { "Q_DECLARE_EXTENSION_INTERFACE", "Q_DECLARE_INTERFACE_TOKEN" },
    29. { "Q_SETS", "Q_FLAGS_TOKEN" },
    30. { "Q_CLASSINFO", "Q_CLASSINFO_TOKEN" },
    31. { "Q_INTERFACES", "Q_INTERFACES_TOKEN" },
    32. { "signals", "SIGNALS" },
    33. { "slots", "SLOTS" },
    34. { "Q_SIGNALS", "Q_SIGNALS_TOKEN" },
    35. { "Q_SLOTS", "Q_SLOTS_TOKEN" },
    36. { "Q_PRIVATE_SLOT", "Q_PRIVATE_SLOT_TOKEN" },
    37. { "QT_MOC_COMPAT", "Q_MOC_COMPAT_TOKEN" },
    38. { "Q_INVOKABLE", "Q_INVOKABLE_TOKEN" },
    39. { "Q_SIGNAL", "Q_SIGNAL_TOKEN" },
    40. { "Q_SLOT", "Q_SLOT_TOKEN" },
    41. { "Q_SCRIPTABLE", "Q_SCRIPTABLE_TOKEN" },
    42. { "Q_PRIVATE_PROPERTY", "Q_PRIVATE_PROPERTY_TOKEN" },
    43. { "Q_REVISION", "Q_REVISION_TOKEN" },
    44. { "\n", "NEWLINE" },
    45. { "\"", "QUOTE" },
    46. ......
    47. }

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

    1. //QtInstallDir\Qt5.12.0\5.12.0\Src\qtbase\src\corelib\kernel\qobjectdefs.h
    2. #ifndef QT_ANNOTATE_CLASS
    3. # ifndef Q_COMPILER_VARIADIC_MACROS
    4. # define QT_ANNOTATE_CLASS(type, x)
    5. # else
    6. # define QT_ANNOTATE_CLASS(type, ...)
    7. # endif
    8. .......
    9. #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对象中。

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

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

    1. //QtInstallDir\Qt5.12.0\5.12.0\Src\qtbase\src\tools\moc\moc.cpp
    2. void Moc::parse()
    3. {
    4. .....
    5. case Q_PLUGIN_METADATA_TOKEN://处理Q_PLUGIN_METADATA_TOKEN
    6. parsePluginData(&def);
    7. break;
    8. .....
    9. }
    10. void Moc::parsePluginData(ClassDef *def)
    11. {
    12. next(LPAREN);
    13. QByteArray metaData;
    14. while (test(IDENTIFIER)) {
    15. QByteArray l = lexem();
    16. if (l == "IID") {
    17. next(STRING_LITERAL);
    18. def->pluginData.iid = unquotedLexem();
    19. } else if (l == "FILE") {
    20. next(STRING_LITERAL);
    21. QByteArray metaDataFile = unquotedLexem();
    22. QFileInfo fi(QFileInfo(QString::fromLocal8Bit(currentFilenames.top().constData())).dir(), QString::fromLocal8Bit(metaDataFile.constData()));
    23. for (int j = 0; j < includes.size() && !fi.exists(); ++j) {
    24. const IncludePath &p = includes.at(j);
    25. if (p.isFrameworkPath)
    26. continue;
    27. fi.setFile(QString::fromLocal8Bit(p.path.constData()), QString::fromLocal8Bit(metaDataFile.constData()));
    28. // try again, maybe there's a file later in the include paths with the same name
    29. if (fi.isDir()) {
    30. fi = QFileInfo();
    31. continue;
    32. }
    33. }
    34. if (!fi.exists()) {
    35. const QByteArray msg = "Plugin Metadata file " + lexem()
    36. + " does not exist. Declaration will be ignored";
    37. error(msg.constData());
    38. return;
    39. }
    40. QFile file(fi.canonicalFilePath());
    41. if (!file.open(QFile::ReadOnly)) {
    42. QByteArray msg = "Plugin Metadata file " + lexem() + " could not be opened: "
    43. + file.errorString().toUtf8();
    44. error(msg.constData());
    45. return;
    46. }
    47. metaData = file.readAll();
    48. }
    49. }
    50. if (!metaData.isEmpty()) {
    51. def->pluginData.metaData = QJsonDocument::fromJson(metaData);
    52. if (!def->pluginData.metaData.isObject()) {
    53. const QByteArray msg = "Plugin Metadata file " + lexem()
    54. + " does not contain a valid JSON object. Declaration will be ignored";
    55. warning(msg.constData());
    56. def->pluginData.iid = QByteArray();
    57. return;
    58. }
    59. }
    60. mustIncludeQPluginH = true;
    61. next(RPAREN);
    62. }

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

    1. //QtInstallDir\Src\qtbase\src\tools\moc\generator.h
    2. class Generator
    3. {
    4. FILE *out;
    5. ClassDef *cdef;
    6. QVector<uint> meta_data;
    7. public:
    8. Generator(ClassDef *classDef, const QVector<QByteArray> &metaTypes, const QHash<QByteArray, QByteArray> &knownQObjectClasses, const QHash<QByteArray, QByteArray> &knownGadgets, FILE *outfile = 0);
    9. void generateCode();
    10. private:
    11. bool registerableMetaType(const QByteArray &propertyType);
    12. void registerClassInfoStrings();
    13. void generateClassInfos();
    14. void registerFunctionStrings(const QVector<FunctionDef> &list);
    15. void registerByteArrayVector(const QVector<QByteArray> &list);
    16. void generateFunctions(const QVector<FunctionDef> &list, const char *functype, int type, int &paramsIndex);
    17. void generateFunctionRevisions(const QVector<FunctionDef> &list, const char *functype);
    18. void generateFunctionParameters(const QVector<FunctionDef> &list, const char *functype);
    19. void generateTypeInfo(const QByteArray &typeName, bool allowEmptyName = false);
    20. void registerEnumStrings();
    21. void generateEnums(int index);
    22. void registerPropertyStrings();
    23. void generateProperties();
    24. void generateMetacall();
    25. void generateStaticMetacall();
    26. void generateSignal(FunctionDef *def, int index);
    27. void generatePluginMetaData();
    28. ......
    29. }
    30. ------------------------------------------------------
    31. //QtInstallDir\Src\qtbase\src\tools\moc\generator.cpp
    32. void Generator::generateCode()
    33. {
    34. ..........
    35. //
    36. // Build classinfo array
    37. //
    38. generateClassInfos();
    39. //
    40. // Build signals array first, otherwise the signal indices would be wrong
    41. //
    42. generateFunctions(cdef->signalList, "signal", MethodSignal, paramsIndex);
    43. //
    44. // Build slots array
    45. //
    46. generateFunctions(cdef->slotList, "slot", MethodSlot, paramsIndex);
    47. //
    48. // Build method array
    49. //
    50. generateFunctions(cdef->methodList, "method", MethodMethod, paramsIndex);
    51. //
    52. // Build method version arrays
    53. //
    54. if (cdef->revisionedMethods) {
    55. generateFunctionRevisions(cdef->signalList, "signal");
    56. generateFunctionRevisions(cdef->slotList, "slot");
    57. generateFunctionRevisions(cdef->methodList, "method");
    58. }
    59. //
    60. // Build method parameters array
    61. //
    62. generateFunctionParameters(cdef->signalList, "signal");
    63. generateFunctionParameters(cdef->slotList, "slot");
    64. generateFunctionParameters(cdef->methodList, "method");
    65. if (isConstructible)
    66. generateFunctionParameters(cdef->constructorList, "constructor");
    67. //
    68. // Build property array
    69. //
    70. generateProperties();
    71. //
    72. // Build enums array
    73. //
    74. generateEnums(enumsIndex);
    75. //
    76. // Build constructors array
    77. //
    78. if (isConstructible)
    79. generateFunctions(cdef->constructorList, "constructor", MethodConstructor, paramsIndex);
    80. //
    81. // Terminate data array
    82. //
    83. fprintf(out, "\n 0 // eod\n};\n\n");
    84. //
    85. // Generate internal qt_static_metacall() function
    86. //
    87. const bool hasStaticMetaCall = !isQt &&
    88. (cdef->hasQObject || !cdef->methodList.isEmpty()
    89. || !cdef->propertyList.isEmpty() || !cdef->constructorList.isEmpty());
    90. if (hasStaticMetaCall)
    91. generateStaticMetacall();
    92. //
    93. // Build extra array
    94. //
    95. .......
    96. }
    97. void Generator::generatePluginMetaData()
    98. {
    99. if (cdef->pluginData.iid.isEmpty())
    100. return;
    101. fputs("\nQT_PLUGIN_METADATA_SECTION\n"
    102. "static constexpr unsigned char qt_pluginMetaData[] = {\n"
    103. " 'Q', 'T', 'M', 'E', 'T', 'A', 'D', 'A', 'T', 'A', ' ', '!',\n"
    104. " // metadata version, Qt version, architectural requirements\n"
    105. " 0, QT_VERSION_MAJOR, QT_VERSION_MINOR, qPluginArchRequirements(),", out);
    106. CborDevice dev(out);
    107. CborEncoder enc;
    108. cbor_encoder_init_writer(&enc, CborDevice::callback, &dev);
    109. CborEncoder map;
    110. cbor_encoder_create_map(&enc, &map, CborIndefiniteLength);
    111. dev.nextItem("\"IID\"");
    112. cbor_encode_int(&map, int(QtPluginMetaDataKeys::IID));
    113. cbor_encode_text_string(&map, cdef->pluginData.iid.constData(), cdef->pluginData.iid.size());
    114. dev.nextItem("\"className\"");
    115. cbor_encode_int(&map, int(QtPluginMetaDataKeys::ClassName));
    116. cbor_encode_text_string(&map, cdef->classname.constData(), cdef->classname.size());
    117. QJsonObject o = cdef->pluginData.metaData.object();
    118. if (!o.isEmpty()) {
    119. dev.nextItem("\"MetaData\"");
    120. cbor_encode_int(&map, int(QtPluginMetaDataKeys::MetaData));
    121. jsonObjectToCbor(&map, o);
    122. }
    123. // Add -M args from the command line:
    124. for (auto it = cdef->pluginData.metaArgs.cbegin(), end = cdef->pluginData.metaArgs.cend(); it != end; ++it) {
    125. const QJsonArray &a = it.value();
    126. QByteArray key = it.key().toUtf8();
    127. dev.nextItem(QByteArray("command-line \"" + key + "\"").constData());
    128. cbor_encode_text_string(&map, key.constData(), key.size());
    129. jsonArrayToCbor(&map, a);
    130. }
    131. // Close the CBOR map manually
    132. dev.nextItem();
    133. cbor_encoder_close_container(&enc, &map);
    134. fputs("\n};\n", out);
    135. // 'Use' all namespaces.
    136. int pos = cdef->qualified.indexOf("::");
    137. for ( ; pos != -1 ; pos = cdef->qualified.indexOf("::", pos + 2) )
    138. fprintf(out, "using namespace %s;\n", cdef->qualified.left(pos).constData());
    139. fprintf(out, "QT_MOC_EXPORT_PLUGIN(%s, %s)\n\n",
    140. cdef->qualified.constData(), cdef->classname.constData());
    141. }
    142. void Generator::generateMetacall()
    143. {
    144. bool isQObject = (cdef->classname == "QObject");
    145. fprintf(out, "\nint %s::qt_metacall(QMetaObject::Call _c, int _id, void **_a)\n{\n",
    146. cdef->qualified.constData());
    147. if (!purestSuperClass.isEmpty() && !isQObject) {
    148. QByteArray superClass = purestSuperClass;
    149. fprintf(out, " _id = %s::qt_metacall(_c, _id, _a);\n", superClass.constData());
    150. }
    151. bool needElse = false;
    152. QVector<FunctionDef> methodList;
    153. methodList += cdef->signalList;
    154. methodList += cdef->slotList;
    155. methodList += cdef->methodList;
    156. // If there are no methods or properties, we will return _id anyway, so
    157. // don't emit this comparison -- it is unnecessary, and it makes coverity
    158. // unhappy.
    159. if (methodList.size() || cdef->propertyList.size()) {
    160. fprintf(out, " if (_id < 0)\n return _id;\n");
    161. }
    162. ...........
    163. }

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

    1. //youProjectPath\moc_CalPlugin.cpp
    2. //moc 识别到原有代码中的有效的Q_PLUGIN_METADATA标识后,在moc_*.cpp中生成的相关代码
    3. QT_PLUGIN_METADATA_SECTION
    4. static constexpr unsigned char qt_pluginMetaData[] = {
    5. 'Q', 'T', 'M', 'E', 'T', 'A', 'D', 'A', 'T', 'A', ' ', '!',
    6. // metadata version, Qt version, architectural requirements
    7. 0, QT_VERSION_MAJOR, QT_VERSION_MINOR, qPluginArchRequirements(),
    8. 0xbf,
    9. // "IID"
    10. 0x02, 0x78, 0x1c, 'E', 'x', 'a', 'm', 'p',
    11. 'l', 'e', 's', '.', 'P', 'l', 'u', 'g',
    12. 'i', 'n', '.', 'C', 'a', 'l', 'I', 'n',
    13. 't', 'e', 'r', 'f', 'a', 'c', 'e',
    14. // "className"
    15. 0x03, 0x69, 'C', 'a', 'l', 'P', 'l', 'u',
    16. 'g', 'i', 'n',
    17. 0xff,
    18. };
    19. QT_MOC_EXPORT_PLUGIN(CalPlugin, CalPlugin)
    1. //QtInstallDir\Src\qtbase\src\corelib\plugin\qplugin.h
    2. #define Q_IMPORT_PLUGIN(PLUGIN) \ //与静态库配套使用的宏,该宏使用可以参考plugandpaint example中main.cpp
    3. extern const QT_PREPEND_NAMESPACE(QStaticPlugin) qt_static_plugin_##PLUGIN(); \
    4. class Static##PLUGIN##PluginInstance{ \
    5. public: \
    6. Static##PLUGIN##PluginInstance() { \
    7. qRegisterStaticPluginFunction(qt_static_plugin_##PLUGIN()); \
    8. } \
    9. }; \
    10. static Static##PLUGIN##PluginInstance static##PLUGIN##Instance;
    11. #define Q_PLUGIN_INSTANCE(IMPLEMENTATION) \
    12. { \
    13. static QT_PREPEND_NAMESPACE(QPointer)<QT_PREPEND_NAMESPACE(QObject)> _instance; \
    14. if (!_instance) { \
    15. QT_PLUGIN_RESOURCE_INIT \
    16. _instance = new IMPLEMENTATION; \
    17. } \
    18. return _instance; \
    19. }
    20. #if defined(QT_STATICPLUGIN) //QT_MOC_EXPORT_PLUGIN宏根据QT_STATICPLUGIN来判定该生成静态库相关函数还是动态库相关函数。
    21. # define QT_MOC_EXPORT_PLUGIN(PLUGINCLASS, PLUGINCLASSNAME) \ //生成静态库函数所需函数
    22. static QT_PREPEND_NAMESPACE(QObject) *qt_plugin_instance_##PLUGINCLASSNAME() \
    23. Q_PLUGIN_INSTANCE(PLUGINCLASS) \
    24. static const char *qt_plugin_query_metadata_##PLUGINCLASSNAME() { return reinterpret_cast(qt_pluginMetaData); } \
    25. const QT_PREPEND_NAMESPACE(QStaticPlugin) qt_static_plugin_##PLUGINCLASSNAME() { \
    26. QT_PREPEND_NAMESPACE(QStaticPlugin) plugin = { qt_plugin_instance_##PLUGINCLASSNAME, qt_plugin_query_metadata_##PLUGINCLASSNAME}; \
    27. return plugin; \
    28. }
    29. #else
    30. # define QT_MOC_EXPORT_PLUGIN(PLUGINCLASS, PLUGINCLASSNAME) \ //生成动态库函数所需函数
    31. Q_EXTERN_C Q_DECL_EXPORT \
    32. const char *qt_plugin_query_metadata() \
    33. { return reinterpret_cast(qt_pluginMetaData); } \
    34. Q_EXTERN_C Q_DECL_EXPORT QT_PREPEND_NAMESPACE(QObject) *qt_plugin_instance() \
    35. Q_PLUGIN_INSTANCE(PLUGINCLASS)
    36. #endif

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

    1. //QtInstallDir\Src\qtbase\src\corelib\plugin\qlibrary.cpp
    2. bool QLibraryPrivate::loadPlugin()
    3. {
    4. if (instance) {
    5. libraryUnloadCount.ref();
    6. return true;
    7. }
    8. if (pluginState == IsNotAPlugin)
    9. return false;
    10. if (load()) {
    11. instance = (QtPluginInstanceFunction)resolve("qt_plugin_instance");
    12. return instance;
    13. }
    14. if (qt_debug_component())
    15. qWarning() << "QLibraryPrivate::loadPlugin failed on" << fileName << ":" << errorString;
    16. pluginState = IsNotAPlugin;
    17. return false;
    18. }
    19. QFunctionPointer QLibraryPrivate::resolve(const char *symbol)
    20. {
    21. if (!pHnd)
    22. return 0;
    23. return resolve_sys(symbol);
    24. }
    25. -----------------------------------
    26. //QtInstallDir\Src\qtbase\src\corelib\plugin\qlibrary_win.cpp
    27. QFunctionPointer QLibraryPrivate::resolve_sys(const char* symbol)
    28. {
    29. FARPROC address = GetProcAddress(pHnd, symbol);
    30. if (!address) {
    31. errorString = QLibrary::tr("Cannot resolve symbol \"%1\" in %2: %3").arg(
    32. QString::fromLatin1(symbol), QDir::toNativeSeparators(fileName), qt_error_string());
    33. } else {
    34. errorString.clear();
    35. }
    36. return QFunctionPointer(address);
    37. }

    二、Q_INTERFACE

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

    1. //QtInstallDir\Qt5.12.0\5.12.0\msvc2015_64\include\QtCore\qobjectdefs.h
    2. ......
    3. #ifndef QT_ANNOTATE_CLASS
    4. # ifndef Q_COMPILER_VARIADIC_MACROS
    5. # define QT_ANNOTATE_CLASS(type, x)
    6. # else
    7. # define QT_ANNOTATE_CLASS(type, ...)
    8. # endif
    9. #endif
    10. ........
    11. #define Q_INTERFACES(x) QT_ANNOTATE_CLASS(qt_interfaces, x)
    12. ......

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

    1. //QtInstallDir\Src\qtbase\src\tools\moc\moc.h
    2. ......
    3. struct Interface
    4. {
    5. Interface() {} // for QVector, don't use
    6. inline explicit Interface(const QByteArray &_className)
    7. : className(_className) {}
    8. QByteArray className;
    9. QByteArray interfaceId;
    10. };
    11. QVector >interfaceList;
    12. ......
    13. ------------------------
    14. //QtInstallDir\Src\qtbase\src\tools\moc\moc.cpp
    15. void Moc::parse()
    16. {
    17. .......
    18. case Q_INTERFACES_TOKEN:
    19. parseInterfaces(&def);
    20. break;
    21. .......
    22. }
    23. void Moc::parseInterfaces(ClassDef *def)
    24. {
    25. next(LPAREN);
    26. while (test(IDENTIFIER)) { //interface可以一次声明多个Q_INTERFACES(BrushInterface ShapeInterface FilterInterface)
    27. QVector iface;
    28. iface += ClassDef::Interface(lexem());
    29. while (test(SCOPE)) {
    30. iface.last().className += lexem();
    31. next(IDENTIFIER);
    32. iface.last().className += lexem();
    33. }
    34. while (test(COLON)) {
    35. next(IDENTIFIER);
    36. iface += ClassDef::Interface(lexem());
    37. while (test(SCOPE)) {
    38. iface.last().className += lexem();
    39. next(IDENTIFIER);
    40. iface.last().className += lexem();
    41. }
    42. }
    43. // resolve from classnames to interface ids
    44. for (int i = 0; i < iface.count(); ++i) {
    45. const QByteArray iid = interface2IdMap.value(iface.at(i).className);
    46. if (iid.isEmpty())
    47. error("Undefined interface");
    48. iface[i].interfaceId = iid;
    49. }
    50. def->interfaceList += iface;
    51. }
    52. next(RPAREN);
    53. }

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

    1. //QtInstallDir\Src\qtbase\src\tools\moc\generator.cpp
    2. void Generator::generateCode(){
    3. .......
    4. fprintf(out, "\nvoid *%s::qt_metacast(const char *_clname)\n{\n", cdef->qualified.constData());
    5. fprintf(out, " if (!_clname) return nullptr;\n");
    6. fprintf(out, " if (!strcmp(_clname, qt_meta_stringdata_%s.stringdata0))\n"
    7. " return static_cast(this);\n",
    8. qualifiedClassNameIdentifier.constData());
    9. for (int i = 1; i < cdef->superclassList.size(); ++i) { // for all superclasses but the first one
    10. if (cdef->superclassList.at(i).second == FunctionDef::Private)
    11. continue;
    12. const char *cname = cdef->superclassList.at(i).first.constData();
    13. fprintf(out, " if (!strcmp(_clname, \"%s\"))\n return static_cast< %s*>(this);\n",
    14. cname, cname);
    15. }
    16. for (int i = 0; i < cdef->interfaceList.size(); ++i) {
    17. const QVector<ClassDef::Interface> &iface = cdef->interfaceList.at(i);
    18. for (int j = 0; j < iface.size(); ++j) {
    19. fprintf(out, " if (!strcmp(_clname, %s))\n return ", iface.at(j).interfaceId.constData());
    20. for (int k = j; k >= 0; --k)
    21. fprintf(out, "static_cast< %s*>(", iface.at(k).className.constData());
    22. fprintf(out, "this%s;\n", QByteArray(j + 1, ')').constData());
    23. }
    24. }
    25. if (!purestSuperClass.isEmpty() && !isQObject) {
    26. QByteArray superClass = purestSuperClass;
    27. fprintf(out, " return %s::qt_metacast(_clname);\n", superClass.constData());
    28. } else {
    29. fprintf(out, " return nullptr;\n");
    30. }
    31. fprintf(out, "}\n");
    32. ......
    33. }

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

    1. //QtInstalldir/Qt5.12.0\Examples\Qt-5.12.0\widgets\tools\plugandpaint\plugins\basictools\basictoolsplugin.h
    2. ......
    3. Q_INTERFACES(BrushInterface ShapeInterface FilterInterface) //一次性同时声明多个interface
    4. ......
    1. //QtInstalldir/Qt5.12.0\Examples\Qt-5.12.0\widgets\tools\plugandpaint\plugins\basictools\moc_basictoolsplugin.cpp
    2. void *BasicToolsPlugin::qt_metacast(const char *_clname)
    3. {
    4. if (!_clname) return nullptr;
    5. if (!strcmp(_clname, qt_meta_stringdata_BasicToolsPlugin.stringdata0))
    6. return static_cast<void*>(this);
    7. if (!strcmp(_clname, "BrushInterface"))
    8. return static_cast< BrushInterface*>(this);
    9. if (!strcmp(_clname, "ShapeInterface"))
    10. return static_cast< ShapeInterface*>(this);
    11. if (!strcmp(_clname, "FilterInterface"))
    12. return static_cast< FilterInterface*>(this);
    13. if (!strcmp(_clname, "org.qt-project.Qt.Examples.PlugAndPaint.BrushInterface/1.0"))
    14. return static_cast< BrushInterface*>(this);
    15. if (!strcmp(_clname, "org.qt-project.Qt.Examples.PlugAndPaint.ShapeInterface/1.0"))
    16. return static_cast< ShapeInterface*>(this);
    17. if (!strcmp(_clname, "org.qt-project.Qt.Examples.PlugAndPaint.FilterInterface/1.0"))
    18. return static_cast< FilterInterface*>(this);
    19. return QObject::qt_metacast(_clname);
    20. }
    21. .......
    22. QT_PLUGIN_METADATA_SECTION
    23. static constexpr unsigned char qt_pluginMetaData[] = {
    24. 'Q', 'T', 'M', 'E', 'T', 'A', 'D', 'A', 'T', 'A', ' ', '!',
    25. // metadata version, Qt version, architectural requirements
    26. 0, QT_VERSION_MAJOR, QT_VERSION_MINOR, qPluginArchRequirements(),
    27. 0xbf,
    28. // "IID"
    29. 0x02, 0x78, 0x36, 'o', 'r', 'g', '.', 'q',
    30. 't', '-', 'p', 'r', 'o', 'j', 'e', 'c',
    31. 't', '.', 'Q', 't', '.', 'E', 'x', 'a',
    32. 'm', 'p', 'l', 'e', 's', '.', 'P', 'l',
    33. 'u', 'g', 'A', 'n', 'd', 'P', 'a', 'i',
    34. 'n', 't', '.', 'B', 'r', 'u', 's', 'h',
    35. 'I', 'n', 't', 'e', 'r', 'f', 'a', 'c',
    36. 'e',
    37. // "className"
    38. 0x03, 0x70, 'B', 'a', 's', 'i', 'c', 'T',
    39. 'o', 'o', 'l', 's', 'P', 'l', 'u', 'g',
    40. 'i', 'n',
    41. 0xff,
    42. };
    43. 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.

    1. QtInstallDir\Src\qtbase\src\corelib\kernel\qobject.h
    2. ......
    3. template <class T>
    4. inline T qobject_cast(QObject *object)
    5. {
    6. typedef typename std::remove_cv<typename std::remove_pointer<T>::type>::type ObjType;
    7. Q_STATIC_ASSERT_X(QtPrivate::HasQ_OBJECT_Macro<ObjType>::Value,
    8. "qobject_cast requires the type to have a Q_OBJECT macro");
    9. return static_cast<T>(ObjType::staticMetaObject.cast(object));
    10. }
    11. template <class T>
    12. inline T qobject_cast(const QObject *object)
    13. {
    14. typedef typename std::remove_cv<typename std::remove_pointer<T>::type>::type ObjType;
    15. Q_STATIC_ASSERT_X(QtPrivate::HasQ_OBJECT_Macro<ObjType>::Value,
    16. "qobject_cast requires the type to have a Q_OBJECT macro");
    17. return static_cast<T>(ObjType::staticMetaObject.cast(object));
    18. }
    19. template <class T> inline const char * qobject_interface_iid()
    20. { return nullptr; }
    21. #if !defined(Q_MOC_RUN) && !defined(Q_CLANG_QDOC)
    22. # define Q_DECLARE_INTERFACE(IFace, IId) \
    23. template <> inline const char *qobject_interface_iid<IFace *>() \
    24. { return IId; } \
    25. template <> inline IFace *qobject_cast<IFace *>(QObject *object) \
    26. { return reinterpret_cast<IFace *>((object ? object->qt_metacast(IId) : nullptr)); } \
    27. template <> inline IFace *qobject_cast<IFace *>(const QObject *object) \
    28. { return reinterpret_cast<IFace *>((object ? const_cast<QObject *>(object)->qt_metacast(IId) : nullptr)); }
    29. #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内容如下:

    1. {
    2. "FirstName": "John",
    3. "LastName": "Doe",
    4. "Age": 43,
    5. "Address": {
    6. "Street": "Downing Street 10",
    7. "City": "London",
    8. "Country": "Great Britain"
    9. },
    10. "Phone numbers": [
    11. "+44 1234567",
    12. "+44 2345678"
    13. ]
    14. }

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

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

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

    1. //moc_*.cpp
    2. QT_PLUGIN_METADATA_SECTION
    3. static constexpr unsigned char qt_pluginMetaData[] = {
    4. 'Q', 'T', 'M', 'E', 'T', 'A', 'D', 'A', 'T', 'A', ' ', '!',
    5. // metadata version, Qt version, architectural requirements
    6. 0, QT_VERSION_MAJOR, QT_VERSION_MINOR, qPluginArchRequirements(),
    7. 0xbf,
    8. // "IID"
    9. 0x02, 0x78, 0x3b, 'o', 'r', 'g', '.', 'q',
    10. 't', '-', 'p', 'r', 'o', 'j', 'e', 'c',
    11. 't', '.', 'Q', 't', '.', 'E', 'x', 'a',
    12. 'm', 'p', 'l', 'e', 's', '.', 'P', 'l',
    13. 'u', 'g', 'A', 'n', 'd', 'P', 'a', 'i',
    14. 'n', 't', '.', 'F', 'i', 'l', 't', 'e',
    15. 'r', 'I', 'n', 't', 'e', 'r', 'f', 'a',
    16. 'c', 'e', '/', '1', '.', '0',
    17. // "className"
    18. 0x03, 0x72, 'E', 'x', 't', 'r', 'a', 'F',
    19. 'i', 'l', 't', 'e', 'r', 's', 'P', 'l',
    20. 'u', 'g', 'i', 'n',
    21. // "MetaData"
    22. 0x04, 0xa5, 0x67, 'A', 'd', 'd', 'r', 'e',
    23. 's', 's', 0xa3, 0x64, 'C', 'i', 't', 'y',
    24. 0x66, 'L', 'o', 'n', 'd', 'o', 'n', 0x67,
    25. 'C', 'o', 'u', 'n', 't', 'r', 'y', 0x6d,
    26. 'G', 'r', 'e', 'a', 't', ' ', 'B', 'r',
    27. 'i', 't', 'a', 'i', 'n', 0x66, 'S', 't',
    28. 'r', 'e', 'e', 't', 0x71, 'D', 'o', 'w',
    29. 'n', 'i', 'n', 'g', ' ', 'S', 't', 'r',
    30. 'e', 'e', 't', ' ', '1', '0', 0x63, 'A',
    31. 'g', 'e', 0x18, 0x2b, 0x69, 'F', 'i', 'r',
    32. 's', 't', 'N', 'a', 'm', 'e', 0x64, 'J',
    33. 'o', 'h', 'n', 0x68, 'L', 'a', 's', 't',
    34. 'N', 'a', 'm', 'e', 0x63, 'D', 'o', 'e',
    35. 0x6d, 'P', 'h', 'o', 'n', 'e', ' ', 'n',
    36. 'u', 'm', 'b', 'e', 'r', 's', 0x82, 0x6b,
    37. '+', '4', '4', ' ', '1', '2', '3', '4',
    38. '5', '6', '7', 0x6b, '+', '4', '4', ' ',
    39. '2', '3', '4', '5', '6', '7', '8',
    40. // command-line "yy"
    41. 0x62, 'y', 'y', 0x81, 0x64, 't', 't', 'e',
    42. 't',
    43. 0xff,
    44. };
    45. QT_MOC_EXPORT_PLUGIN(CalPlugin, CalPlugin)

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

  • 相关阅读:
    第十六章 类和对象——运算符重载
    机器学习极简介绍(二)
    《深入理解计算机系统》读书笔记1.1-1.5
    数据库面试题+sql语句解析
    Docker (六)【Docker Compose】
    PHP:构造函数和析构函数
    [python] Python日志记录库loguru使用指北
    SVG图形
    以人为本 养老服务“榕城为老服务社群体验馆”举办挂牌仪式
    Springboot给每个接口设置traceId,并添加到返回结果中
  • 原文地址:https://blog.csdn.net/qiushangren/article/details/126657309