This is the 30th day of my participation in the August Text Challenge.More challenges in August
1, an overview of the
The previous article showed a simple example of QT low-API plug-ins, but this is not suitable for large application scenarios and does not extend. Communication between plug-ins, loading and unloading (freeing memory), plug-in metadata, plug-in life cycle, plug-in dependencies, and so on, are what we do. Inside QT, high-level apis have pluginManagers to do this, but low-level apis need to write their own plug-in managers to help us solve these problems.
Imagine a Windows computer, complete with a host, screen, keyboard and mouse. If we unplug the keyboard, the computer will not make a mistake, only the function of the keyboard is missing, so the keyboard can be regarded as a plug-in. At the same time, a complete computer includes not only the keyboard and mouse, but also the parts such as earphones, audio, optical drive and graphics cards. These parts can be regarded as plug-ins. For Windows, these “plug-ins” have a manager, the Device Manager. The device manager is responsible for adding and removing all hardware and drivers from a computer, so you can think of the device manager as a plug-in manager. Finally, every computer system has its own kernel. A Windows system responds from startup to shutdown by the kernel, which acts as the main program for loading plug-ins, as if: “Once you plug it in, I’ll use you to play games.”
As you can see from the above example, a plug-in system has three major aspects:
The red circle in the figure above indicates a layer, the corresponding Windows driver layer, which is responsible for communicating with the kernel. The corresponding plug-in system is called “Adapter”, which is responsible for plug-in communication and can avoid the interdependent coupling between plug-ins. I’ll talk about that in the next video.
2, the instance,
First, the following code and class names are written in imitation of QT source code (with some imperfections, of course) :
#qtpluginmanager.h #ifndef QTPLUGINSMANAGER_H #define QTPLUGINSMANAGER_H #include <QObject> #include <QPluginLoader> #include <QVariant> #include "qtpluginsmanagerprivate.h" #include <QDir> class QtPluginsManager : public QObject { Q_OBJECT public: static QtPluginsManager &getInstance() { static QtPluginsManager m_instance; return m_instance; } public: // get the plugin directory QDir getPluginPath(); // loadAllPlugins void loadAllPlugins(); // Scan the plugin metadata in the JSON file void scan(const QString &filepath); // Load one of these plugins void loadPlugin(const QString &filepath); Void unloadAllPlugins(); Void unloadPlugin(const QString &filepath); QList<QPluginLoader *> allPlugins(); QPluginLoader* getPlugin(const QString &name); private: QtPluginsManager() { d = new QtPluginsManagerPrivate; }; QtPluginsManagerPrivate *d; }; #endifCopy the code
# qtpluginsmanagerprivate.h #ifndef QTPLUGINSMANAGERPRIVATE_H #define QTPLUGINSMANAGERPRIVATE_H #include <QString> #include <QVariant> #include <QDebug> #include <QPluginLoader> class QtPluginsManagerPrivate { public: // Plug-in dependency detection bool check(const QString &filepath); QHash<QString, QVariant> m_names; // Plugin path -- plugin name QHash<QString, QVariant> m_versions; QHash<QString, QVariantList>m_dependencies; QHash<QString, QPluginLoader *>m_loaders; // Plugin path --QPluginLoader instance}; #endifCopy the code
The QtPluginsManagerPrivate class contains a collection of metadata for all plug-ins, known as metadata, that comes from json files that we defined in advance and must end with “. Json “. Internally, this JSON file declares metadata as part of plug-in instantiation by the macro Q_PLUGIN_METADATA. More important, though, is the m_Dependencies collection, which contains the dependencies of plug-ins to determine the order in which they are loaded and whether they need to be loaded.
#qtpluginmanager.cpp void QtPluginsManager::loadAllPlugins() { QDir path = QDir(qApp->applicationDirPath()); path.cd(".. /plugins"); Foreach (QFileInfo info, path. EntryInfoList (QDir: : Files | QDir: : NoDotAndDotDot)) {/ / scan all plug-ins, Scan (info.absoluteFilepath ()); } foreach (QFileInfo info, Path. EntryInfoList (QDir: : Files | QDir: : NoDotAndDotDot)) {/ / load the plug-in loadPlugin (info. AbsoluteFilePath ()); }}Copy the code
Void QtPluginsManager::scan(const QString& path) {/** * Windows:.dll,.dll * Unix/Linux:.so **/ if (! QLibrary::isLibrary(path)) return; // Get metadata (name, version, dependency) QPluginLoader *loader = new QPluginLoader(path); QJsonObject json = loader->metaData().value("MetaData").toObject(); d->m_names.insert(path, json.value("name").toVariant()); d->m_versions.insert(path, json.value("version").toVariant()); d->m_dependencies.insert(path, json.value("dependencies").toArray().toVariantList()); delete loader; loader = Q_NULLPTR; }Copy the code
# qtpluginmanager. CPP / / load the plug-in void QtPluginsManager: : loadPlugin (const QString & path) {/ / judge whether libraries if (! QLibrary::isLibrary(path)) return; // Check plugin dependencies (recursive calls) if (! d->check(path)) return; QPluginLoader = new QPluginLoader(path); If (loader->load()) {// If (loader->load()) {// If (loader->load()) {// If (loader->load()) { InterfacePlugin *plugin = qobject_cast<InterfacePlugin *>(loader->instance()); if (plugin) { d->m_loaders.insert(path, loader); plugin->output("plugin"); } else { delete loader; loader = Q_NULLPTR; }}}Copy the code
# qtpluginsmanagerprivate. CPP # include "qtpluginsmanagerprivate. H" / / testing plug-ins depend on the bool qtpluginsmanagerprivate: : check (const QString& path) { bool status = true; foreach (QVariant item, m_dependencies.value(path)) { QVariantMap map = item.toMap(); QVariant Name = map.value("name"); QVariant version = map.value("version"); QString path = m_names.key(name); / * * * * * * * * * * to test whether the plug-in dependencies on other plug-ins * * * * * * * * * * / / / to test the plug-in name if (! m_names.values().contains(name)) { qDebug() << Q_FUNC_INFO << " Missing dependency:" << name.toString() << "for plugin" << path; status = false; continue; } if (m_versions.value(path)! = version) { qDebug() << Q_FUNC_INFO << " Version mismatch:" << name.toString() << "version" << m_versions.value(m_names.key(name)).toString() << "but" << version.toString() << "required for plugin" << path; status = false; continue; } // Then check whether the dependent plug-in also depends on another plug-in if (! check(path)) { qDebug() << Q_FUNC_INFO << "Corrupted dependency:" << name.toString() << "for plugin" << path; status = false; continue; } } return status; }Copy the code
QT internal plug-in version management is more strict, as Demo will not be discussed in depth. See: Qt Documentation Snapshots: Plugin Meta Data.
Check method to check whether the missing some dependent plug-ins, check whether the plug-in version match, if A dependent on the B, B depends on the C, if the plugin is missing, C is A plug-in will not load correctly (because it is possible in A constructor is used to B, B constructor used to C), therefore, Plug-ins should be as dependent as possible.
Reference: one to, two three li: build their own Qt plug-in system in-depth understanding of QtCreator plug-in design architecture
The Demo address: download.csdn.net/download/u0… Github.com/qht10030778…