/* This file is part of the KDE project SPDX-FileCopyrightText: 2007 Matthias Kretz SPDX-FileCopyrightText: 2007 Bernhard Loos SPDX-FileCopyrightText: 2021-2023 Alexander Lohnau SPDX-License-Identifier: LGPL-2.0-or-later */ #ifndef KPLUGINFACTORY_H #define KPLUGINFACTORY_H #include "kcoreaddons_export.h" #include "kpluginmetadata.h" #include #include #include #include class QWidget; class KPluginFactoryPrivate; namespace KParts { class Part; } #define KPluginFactory_iid "org.kde.KPluginFactory" // Internal macro that generated the KPluginFactory subclass #define __K_PLUGIN_FACTORY_DEFINITION(name, pluginRegistrations, ...) \ class name : public KPluginFactory \ { \ Q_OBJECT \ Q_INTERFACES(KPluginFactory) \ Q_PLUGIN_METADATA(__VA_ARGS__) \ public: \ explicit name() \ { \ pluginRegistrations \ } \ ~name() { }; \ }; /*! * \macro K_PLUGIN_FACTORY * \relates KPluginFactory * * Create a KPluginFactory subclass and export it as the root plugin object. * * \a name the name of the KPluginFactory derived class. * * \a pluginRegistrations code to be inserted into the constructor of the * class. Usually a series of registerPlugin() calls. * * \note K_PLUGIN_FACTORY declares the subclass including a Q_OBJECT macro. * So you need to make sure to have Qt's moc run also for the source file * where you use the macro. E.g. in projects using CMake and it's automoc feature, * as usual you need to have a line * \code * #include * \endcode * in the same source file when that one has the name "myplugin.cpp". * * Example: * \code * #include * #include * * class MyPlugin : public PluginInterface * { * public: * MyPlugin(QObject *parent, const QVariantList &args) * : PluginInterface(parent) * {} * }; * * K_PLUGIN_FACTORY(MyPluginFactory, registerPlugin();) * * #include * \endcode * * If you want to compile a .json file into the plugin, use K_PLUGIN_FACTORY_WITH_JSON. * * \sa K_PLUGIN_FACTORY_WITH_JSON */ #define K_PLUGIN_FACTORY(name, pluginRegistrations) __K_PLUGIN_FACTORY_DEFINITION(name, pluginRegistrations, IID KPluginFactory_iid) /*! * \macro K_PLUGIN_FACTORY_WITH_JSON * \relates KPluginFactory * * Create a KPluginFactory subclass and export it as the root plugin object with * JSON metadata. * * This macro does the same as K_PLUGIN_FACTORY, but adds a JSON file as plugin * metadata. See Q_PLUGIN_METADATA() for more information. * * \a name the name of the KPluginFactory derived class. * * \a pluginRegistrations code to be inserted into the constructor of the * class. Usually a series of registerPlugin() calls. * * \a jsonFile name of the JSON file to be compiled into the plugin as metadata * * \note K_PLUGIN_FACTORY_WITH_JSON declares the subclass including a Q_OBJECT macro. * So you need to make sure to have Qt's moc run also for the source file * where you use the macro. E.g. in projects using CMake and its automoc feature, * as usual you need to have a line * \code * #include * \endcode * in the same source file when that one has the name "myplugin.cpp". * * Example: * \code * #include * #include * * class MyPlugin : public PluginInterface * { * public: * MyPlugin(QObject *parent, const KPluginMetaData &metaData, const QVariantList &args) * : PluginInterface(parent) * {} * }; * * K_PLUGIN_FACTORY_WITH_JSON(MyPluginFactory, * "metadata.json", * registerPlugin(); * ) * * #include * \endcode * * \sa K_PLUGIN_FACTORY * * \since 5.0 */ #define K_PLUGIN_FACTORY_WITH_JSON(name, jsonFile, pluginRegistrations) \ __K_PLUGIN_FACTORY_DEFINITION(name, pluginRegistrations, IID KPluginFactory_iid FILE jsonFile) /*! * \macro K_PLUGIN_CLASS_WITH_JSON * \relates KPluginFactory * * Create a KPluginFactory subclass and export it as the root plugin object with * JSON metadata. * * This macro does the same as K_PLUGIN_FACTORY_WITH_JSON, but you only have to pass the class name and the json file. * The factory name and registerPlugin call are deduced from the class name. * * \code * #include * \endcode * in the same source file when that one has the name "myplugin.cpp". * * Example: * \code * #include * #include * * class MyPlugin : public PluginInterface * { * public: * MyPlugin(QObject *parent, const KPluginMetaData &metaData, const QVariantList &args) * : PluginInterface(parent) * {} * }; * * K_PLUGIN_CLASS_WITH_JSON(MyPlugin, "metadata.json") * * #include * \endcode * * \sa K_PLUGIN_FACTORY_WITH_JSON * * \since 5.44 */ #ifdef KPLUGINFACTORY_PLUGIN_CLASS_INTERNAL_NAME #define K_PLUGIN_CLASS_WITH_JSON(classname, jsonFile) \ K_PLUGIN_FACTORY_WITH_JSON(KPLUGINFACTORY_PLUGIN_CLASS_INTERNAL_NAME, jsonFile, registerPlugin();) #else #define K_PLUGIN_CLASS_WITH_JSON(classname, jsonFile) K_PLUGIN_FACTORY_WITH_JSON(classname##Factory, jsonFile, registerPlugin();) #endif /*! * \macro K_PLUGIN_CLASS * \relates KPluginFactory * * Creates a KPluginFactory subclass and exports it as the root plugin object. * Unlike K_PLUGIN_CLASS_WITH_JSON, this macro does not require json meta data. * * This macro does the same as K_PLUGIN_FACTORY, but you only have to pass the class name. * The factory name and registerPlugin call are deduced from the class name. * This is also useful if you want to use static plugins, see the kcoreaddons_add_plugin CMake method. * \since 5.90 */ #ifdef KPLUGINFACTORY_PLUGIN_CLASS_INTERNAL_NAME #define K_PLUGIN_CLASS(classname) K_PLUGIN_FACTORY(KPLUGINFACTORY_PLUGIN_CLASS_INTERNAL_NAME, registerPlugin();) #else #define K_PLUGIN_CLASS(classname) K_PLUGIN_FACTORY(classname##Factory, registerPlugin();) #endif /*! * \class KPluginFactory * \inmodule KCoreAddons * * \brief KPluginFactory provides a convenient way to provide factory-style plugins. * * Qt plugins provide a singleton object, but a common pattern is for plugins * to generate as many objects of a particular type as the application requires. * By using KPluginFactory, you can avoid implementing the factory pattern * yourself. * * KPluginFactory also allows plugins to provide multiple different object * types, indexed by keywords. * * The objects created by KPluginFactory must inherit QObject, and must have a * standard constructor pattern: * \list * \li if the object is a KPart::Part, it must be of the form * \code * T(QWidget *parentWidget, QObject *parent, const QVariantList &args) * \endcode * or * \code * T(QWidget *parentWidget, QObject *parent, const KPluginMetaData &metaData, const QVariantList &args) * \endcode * \li if it is a QWidget, it must be of the form * \code * T(QWidget *parent, const QVariantList &args) * \endcode * or * \code * T(QWidget *parent, const KPluginMetaData &metaData, const QVariantList &args) * \endcode * \li otherwise it must be of the form * \code * T(QObject *parent, const QVariantList &args) * \endcode * or * \code * T(QObject *parent, const KPluginMetaData &metaData, const QVariantList &args) * \endcode * \endlist * You should typically use either K_PLUGIN_CLASS() or * K_PLUGIN_CLASS_WITH_JSON() in your plugin code to generate a factory. * The typical pattern is: * * \code * #include * #include * * class MyPlugin : public PluginInterface * { * public: * MyPlugin(QObject *parent, const QVariantList &args) * : PluginInterface(parent) * {} * }; * * K_PLUGIN_CLASS(MyPlugin) * #include * \endcode * * If you want to write a custom KPluginFactory not using the standard macro(s) * you can reimplement the * create(const char *iface, QWidget *parentWidget, QObject *parent, const QVariantList &args) * method. * * Example: * \code * class SomeScriptLanguageFactory : public KPluginFactory * { * Q_OBJECT * public: * SomeScriptLanguageFactory() * {} * * protected: * virtual QObject *create(const char *iface, QWidget *parentWidget, QObject *parent, const QVariantList &args) * { * // Create an identifier based on the iface and given pluginId * const QString identifier = QLatin1String(iface) + QLatin1Char('_') + metaData().pluginId(); * // load scripting language module from the information in identifier and return it: * return object; * } * }; * \endcode * * To load the KPluginFactory from an installed plugin you can use loadFactory() and for * directly creating a plugin instance from it instantiatePlugin() * */ class KCOREADDONS_EXPORT KPluginFactory : public QObject { Q_OBJECT public: /*! * */ explicit KPluginFactory(); ~KPluginFactory() override; /*! * \since 5.86 * * \value NO_PLUGIN_ERROR No error * \value INVALID_PLUGIN The plugin could not be loaded * \value INVALID_FACTORY The factory object could not be loaded * \value INVALID_KPLUGINFACTORY_INSTANTIATION The target object could not be instantiated */ enum ResultErrorReason { NO_PLUGIN_ERROR = 0, INVALID_PLUGIN, INVALID_FACTORY, INVALID_KPLUGINFACTORY_INSTANTIATION, }; /*! * \class KPluginFactory::Result * \inmodule KCoreAddons * Holds the result of a plugin load operation, i.e. the loaded plugin on success or information about the error on failure * \since 5.86 */ template class Result { public: /*! * \variable KPluginFactory::Result::plugin * \brief The loaded object, or \c nullptr if loading fails */ T *plugin = nullptr; /*! * \variable KPluginFactory::Result::errorString * \brief The translated, user-visible error string */ QString errorString; /*! * \variable KPluginFactory::Result::errorText * \brief The untranslated error text */ QString errorText; /*! * \variable KPluginFactory::Result::errorReason * \brief The error reason */ ResultErrorReason errorReason = NO_PLUGIN_ERROR; /*! * */ explicit operator bool() const { return plugin != nullptr; } }; /*! * Attempts to load the KPluginFactory from the given metadata. * * The errors will be logged using the kf.coreaddons debug category. * * \a data KPluginMetaData from which the plugin should be loaded * * Returns a result object which contains the plugin instance and potentially error information * * \since 5.86 */ static Result loadFactory(const KPluginMetaData &data); /*! * Attempts to load the KPluginFactory and create a T instance from the given metadata. * * KCoreAddons will log error messages automatically, meaning you only need to implement your * own logging in case you want to give it more context info or have a custom category. * * \code * if (auto result = KPluginFactory::instantiatePlugin(metaData, parent, args)) { * // The plugin is valid and result.plugin contains the object * } else { * // We can access the error related properties, but result.plugin is a nullptr * qCWarning(MYCATEGORY) << result.errorString; * } * \endcode * * If there is no extra error handling needed the plugin can be directly accessed and checked if it is a nullptr * \code * if (auto plugin = KPluginFactory::instantiatePlugin(metaData, parent, args).plugin) { * } * \endcode * * \a data KPluginMetaData from which the plugin should be loaded * * \a args arguments which get passed to the plugin's constructor * * Returns a Result object which contains the plugin instance and potentially error information * * \since 5.86 */ template static Result instantiatePlugin(const KPluginMetaData &data, QObject *parent = nullptr, const QVariantList &args = {}) { Result result; KPluginFactory::Result factoryResult = loadFactory(data); if (!factoryResult.plugin) { result.errorString = factoryResult.errorString; result.errorText = factoryResult.errorText; result.errorReason = factoryResult.errorReason; return result; } T *instance = factoryResult.plugin->create(parent, args); if (!instance) { const QLatin1String className(T::staticMetaObject.className()); result.errorString = tr("KPluginFactory could not create a %1 instance from %2").arg(className, data.fileName()); result.errorText = QStringLiteral("KPluginFactory could not create a %1 instance from %2").arg(className, data.fileName()); result.errorReason = INVALID_KPLUGINFACTORY_INSTANTIATION; logFailedInstantiationMessage(T::staticMetaObject.className(), data); } else { result.plugin = instance; } return result; } /*! * Use this method to create an object. * * It will try to create an object which inherits T. * * If it has multiple choices it's not defined which object will be returned, so be careful * to request a unique interface or use keywords. * * T the interface for which an object should be created. The object will inherit T. * * \a parent the parent of the object. If \a parent is a widget type, it will also passed * to the parentWidget argument of the CreateInstanceFunction for the object. * * \a args additional arguments which will be passed to the object. * * Returns a pointer to the created object is returned, or \c nullptr if an error occurred. */ template T *create(QObject *parent = nullptr, const QVariantList &args = {}); /*! * Use this method to create an object. It will try to create an object which inherits T * This overload has an additional \a parentWidget argument, which is used by some plugins (e.g. Parts). * * T the interface for which an object should be created. The object will inherit T. * * \a parentWidget an additional parent widget. * * \a parent the parent of the object. If \a parent is a widget type, it will also passed * to the parentWidget argument of the CreateInstanceFunction for the object. * * \a args additional arguments which will be passed to the object. Since 5.93 this has a default arg. * Returns a pointer to the created object is returned, or \c nullptr if an error occurred. */ template T *create(QWidget *parentWidget, QObject *parent, const QVariantList &args = {}); /*! * Returns the metadata of the plugin * * \since 5.77 */ KPluginMetaData metaData() const; /*! * Set the metadata about the plugin this factory generates. * * \a metaData the metadata about the plugin * * \since 5.77 */ void setMetaData(const KPluginMetaData &metaData); protected: /*! * Function pointer type to a function that instantiates a plugin * * For plugins that don't support a KPluginMetaData parameter it is discarded * \since 5.77 */ using CreateInstanceWithMetaDataFunction = QObject *(*)(QWidget *, QObject *, const KPluginMetaData &, const QVariantList &); /* * This is used to detect the arguments need for the constructor of metadata-taking plugin classes. * You can inherit it, if you want to add new classes and still keep support for the old ones. */ template struct InheritanceWithMetaDataChecker { /// property to control the availability of the registerPlugin overload taking default values static constexpr bool enabled = std::is_constructible::value // KParts || std::is_constructible::value || std::is_constructible::value // QWidgets || std::is_constructible::value || std::is_constructible::value // Nomal QObjects || std::is_constructible::value; CreateInstanceWithMetaDataFunction createInstanceFunction(KParts::Part *) { return &createPartWithMetaDataInstance; } CreateInstanceWithMetaDataFunction createInstanceFunction(QWidget *) { return &createWithMetaDataInstance; } CreateInstanceWithMetaDataFunction createInstanceFunction(...) { return &createWithMetaDataInstance; } }; /* * This is used to detect the arguments need for the constructor of metadata-less plugin classes. * You can inherit it, if you want to add new classes and still keep support for the old ones. */ template struct InheritanceChecker { /// property to control the availability of the registerPlugin overload taking default values static constexpr bool _canConstruct = std::is_constructible::value // QWidget plugin || std::is_constructible::value // || std::is_constructible::value // QObject plugins || std::is_constructible::value; static constexpr bool enabled = _canConstruct && !InheritanceWithMetaDataChecker::enabled; // Avoid ambiguity in case of default arguments CreateInstanceWithMetaDataFunction createInstanceFunction(QWidget *) { return &createInstance; } CreateInstanceWithMetaDataFunction createInstanceFunction(...) { return &createInstance; } }; // Use std::enable_if_t once C++14 can be relied on template using enable_if_t = typename std::enable_if::type; /*! * Uses a default instance creation function depending on the type of interface. If the * interface inherits from * \c KParts::Part the function will call * \code * new T(QWidget *parentWidget, QObject *parent, const QVariantList &args) * \endcode * \c QWidget the function will call * \code * new T(QWidget *parent, const QVariantList &args) * \endcode * else the function will call * \code * new T(QObject *parent, const QVariantList &args) * \endcode * * If those constructor methods are not callable this overload is not available. */ template::enabled, int> = 0> void registerPlugin() { CreateInstanceWithMetaDataFunction instanceFunction = InheritanceChecker().createInstanceFunction(static_cast(nullptr)); registerPlugin(&T::staticMetaObject, instanceFunction); } /*! * Uses a default instance creation function depending on the type of interface. If the * interface inherits from * \c KParts::Part the function will call * \code * new T(QWidget *parentWidget, QObject *parent, const KPluginMetaData &metaData, const QVariantList &args) * \endcode * \c QWidget the function will call * \code * new T(QWidget *parent, const KPluginMetaData &metaData, const QVariantList &args) * \endcode * else the function will call * \code * new T(QObject *parent, const KPluginMetaData &metaData, const QVariantList &args) * \endcode * * If those constructor methods are not callable this overload is not available. */ template::enabled, int> = 0> void registerPlugin() { CreateInstanceWithMetaDataFunction instanceFunction = InheritanceWithMetaDataChecker().createInstanceFunction(static_cast(nullptr)); registerPlugin(&T::staticMetaObject, instanceFunction); } /*! * Registers a plugin with the factory. Call this function from the constructor of the * KPluginFactory subclass to make the create function able to instantiate the plugin when asked * for an interface the plugin implements. * * \a T the name of the plugin class * * \a instanceFunction A function pointer to a function that creates an instance of the plugin. * * \since 5.96 */ template void registerPlugin(CreateInstanceWithMetaDataFunction instanceFunction) { registerPlugin(&T::staticMetaObject, instanceFunction); } /*! * This function is called when the factory asked to create an Object. * * You may reimplement it to provide a very flexible factory. This is especially useful to * provide generic factories for plugins implemented using a scripting language. * * \a iface the staticMetaObject::className() string identifying the plugin interface that * was requested. E.g. for KCModule plugins this string will be "KCModule". * * \a parentWidget only used if the requested plugin is a KPart. * * \a parent the parent object for the plugin object. * * \a args a plugin specific list of arbitrary arguments. */ virtual QObject *create(const char *iface, QWidget *parentWidget, QObject *parent, const QVariantList &args); template static QObject *createInstance(QWidget * /*parentWidget*/, QObject *parent, const KPluginMetaData & /*metaData*/, const QVariantList &args) { ParentType *p = nullptr; if (parent) { p = qobject_cast(parent); Q_ASSERT(p); } if constexpr (std::is_constructible::value) { return new impl(p, args); } else { return new impl(p); } } template static QObject *createWithMetaDataInstance(QWidget * /*parentWidget*/, QObject *parent, const KPluginMetaData &metaData, const QVariantList &args) { ParentType *p = nullptr; if (parent) { p = qobject_cast(parent); Q_ASSERT(p); } if constexpr (std::is_constructible::value) { return new impl(p, metaData, args); } else { return new impl(p, metaData); } } template static QObject *createPartWithMetaDataInstance(QWidget *parentWidget, QObject *parent, const KPluginMetaData &metaData, const QVariantList &args) { if constexpr (std::is_constructible::value) { return new impl(parentWidget, parent, metaData, args); } else { return new impl(parentWidget, parent, metaData); } } private: friend KPluginFactoryPrivate; std::unique_ptr const d; void registerPlugin(const QMetaObject *metaObject, CreateInstanceWithMetaDataFunction instanceFunction); // The logging categories are not part of the public API, consequently this needs to be a private function static void logFailedInstantiationMessage(KPluginMetaData data); static void logFailedInstantiationMessage(const char *className, KPluginMetaData data); }; template inline T *KPluginFactory::create(QObject *parent, const QVariantList &args) { QObject *o = create(T::staticMetaObject.className(), parent && parent->isWidgetType() ? reinterpret_cast(parent) : nullptr, parent, args); T *t = qobject_cast(o); if (!t) { delete o; } return t; } template inline T *KPluginFactory::create(QWidget *parentWidget, QObject *parent, const QVariantList &args) { QObject *o = create(T::staticMetaObject.className(), parentWidget, parent, args); T *t = qobject_cast(o); if (!t) { delete o; } return t; } Q_DECLARE_INTERFACE(KPluginFactory, KPluginFactory_iid) #endif // KPLUGINFACTORY_H