使用版本: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_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 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 ¶msIndex);
- 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
(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
(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宏也具有两重意义,作为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
>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
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
(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也有两重意义,作为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自定义插件需要将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"
设置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博客