From 37b9bf84ebeeff977a970ace4aafde6d33d21935 Mon Sep 17 00:00:00 2001 From: hjk <qtc-committer@nokia.com> Date: Wed, 5 Jan 2011 18:35:08 +0100 Subject: [PATCH] extensionsystem: "soft dependencies" infrastructure Reviewed-by: con Reviewed-by: ckamm --- src/libs/extensionsystem/extensionsystem.pro | 2 + src/libs/extensionsystem/invoker.cpp | 88 ++++++++ src/libs/extensionsystem/invoker.h | 212 +++++++++++++++++++ src/libs/extensionsystem/pluginmanager.cpp | 123 ++++++++++- src/libs/extensionsystem/pluginmanager.h | 5 +- 5 files changed, 420 insertions(+), 10 deletions(-) create mode 100644 src/libs/extensionsystem/invoker.cpp create mode 100644 src/libs/extensionsystem/invoker.h diff --git a/src/libs/extensionsystem/extensionsystem.pro b/src/libs/extensionsystem/extensionsystem.pro index 027139c89ee..de1f49f25b2 100644 --- a/src/libs/extensionsystem/extensionsystem.pro +++ b/src/libs/extensionsystem/extensionsystem.pro @@ -10,6 +10,7 @@ DEFINES += IDE_TEST_DIR=\\\"$$IDE_SOURCE_TREE\\\" HEADERS += pluginerrorview.h \ plugindetailsview.h \ + invoker.h \ iplugin.h \ iplugin_p.h \ extensionsystem_global.h \ @@ -23,6 +24,7 @@ HEADERS += pluginerrorview.h \ plugincollection.h SOURCES += pluginerrorview.cpp \ plugindetailsview.cpp \ + invoker.cpp \ iplugin.cpp \ pluginmanager.cpp \ pluginspec.cpp \ diff --git a/src/libs/extensionsystem/invoker.cpp b/src/libs/extensionsystem/invoker.cpp new file mode 100644 index 00000000000..cbab529c723 --- /dev/null +++ b/src/libs/extensionsystem/invoker.cpp @@ -0,0 +1,88 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** No Commercial Usage +** +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ + +#include "invoker.h" + +namespace ExtensionSystem { + +InvokerBase::InvokerBase() +{ + lastArg = 0; + useRet = false; + nag = true; + success = true; + target = 0; +} + +InvokerBase::~InvokerBase() +{ + if (!success && nag) + qWarning("Could not invoke function '%s' in object of type '%s'.", + sig.constData(), target->metaObject()->className()); +} + +bool InvokerBase::wasSuccessful() const +{ + nag = false; + return success; +} + +void InvokerBase::invoke(QObject *t, const char *slot) +{ + target = t; + success = false; + sig.append(slot, qstrlen(slot)); + sig.append('('); + for (int paramCount = 0; paramCount < lastArg; ++paramCount) { + if (paramCount) + sig.append(','); + const char *type = arg[paramCount].name(); + sig.append(type, strlen(type)); + } + sig.append(')'); + sig.append('\0'); + int idx = target->metaObject()->indexOfMethod(sig.constData()); + if (idx < 0) + return; + QMetaMethod method = target->metaObject()->method(idx); + if (useRet) + success = method.invoke(target, ret, + arg[0], arg[1], arg[2], arg[3], arg[4], + arg[5], arg[6], arg[7], arg[8], arg[9]); + else + success = method.invoke(target, + arg[0], arg[1], arg[2], arg[3], arg[4], + arg[5], arg[6], arg[7], arg[8], arg[9]); +} + +} // namespace ExtensionSystem diff --git a/src/libs/extensionsystem/invoker.h b/src/libs/extensionsystem/invoker.h new file mode 100644 index 00000000000..48815c96ed9 --- /dev/null +++ b/src/libs/extensionsystem/invoker.h @@ -0,0 +1,212 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** No Commercial Usage +** +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ + +#ifndef EXTENSIONSYSTEM_INVOKER_H +#define EXTENSIONSYSTEM_INVOKER_H + +#include "extensionsystem_global.h" + +#include <QtCore/QMetaMethod> +#include <QtCore/QMetaObject> +#include <QtCore/QMetaType> +#include <QtCore/QVarLengthArray> + +namespace ExtensionSystem { + +class InvokerBase +{ +public: + InvokerBase(); + ~InvokerBase(); + + bool wasSuccessful() const; + + template <class T> void addArgument(const T &t) + { + arg[lastArg++] = QGenericArgument(typeName<T>(), &t); + } + + template <class T> void setReturnValue(T &t) + { + useRet = true; + ret = QGenericReturnArgument(typeName<T>(), &t); + } + + void invoke(QObject *target, const char *slot); + +private: + InvokerBase(const InvokerBase &); // Unimplemented. + template <class T> const char *typeName() + { + return QMetaType::typeName(qMetaTypeId<T>()); + } + QObject *target; + QGenericArgument arg[10]; + QGenericReturnArgument ret; + QVarLengthArray<char, 512> sig; + int lastArg; + bool success; + bool useRet; + mutable bool nag; +}; + +template <class Result> +class Invoker : public InvokerBase +{ +public: + Invoker(QObject *target, const char *slot) + { + InvokerBase::invoke(target, slot); + } + + template <class T0> + Invoker(QObject *target, const char *slot, const T0 &t0) + { + setReturnValue(result); + addArgument(t0); + InvokerBase::invoke(target, slot); + } + + template <class T0, class T1> + Invoker(QObject *target, const char *slot, const T0 &t0, const T1 &t1) + { + setReturnValue(result); + addArgument(t0); + addArgument(t1); + InvokerBase::invoke(target, slot); + } + + template <class T0, class T1, class T2> + Invoker(QObject *target, const char *slot, const T0 &t0, + const T1 &t1, const T2 &t2) + { + setReturnValue(result); + addArgument(t0); + addArgument(t1); + addArgument(t2); + InvokerBase::invoke(target, slot); + } + + operator Result() const { return result; } + +private: + Result result; +}; + +template<> class Invoker<void> : public InvokerBase +{ +public: + Invoker(QObject *target, const char *slot) + { + InvokerBase::invoke(target, slot); + } + + template <class T0> + Invoker(QObject *target, const char *slot, const T0 &t0) + { + addArgument(t0); + InvokerBase::invoke(target, slot); + } + + template <class T0, class T1> + Invoker(QObject *target, const char *slot, const T0 &t0, const T1 &t1) + { + addArgument(t0); + addArgument(t1); + InvokerBase::invoke(target, slot); + } + + template <class T0, class T1, class T2> + Invoker(QObject *target, const char *slot, const T0 &t0, + const T1 &t1, const T2 &t2) + { + addArgument(t0); + addArgument(t1); + addArgument(t2); + InvokerBase::invoke(target, slot); + } +}; + +template <class Result> +Result invokeHelper(InvokerBase &in, QObject *target, const char *slot) +{ + Result result; + in.setReturnValue(result); + in.invoke(target, slot); + return result; +} + +template <> +inline void invokeHelper<void>(InvokerBase &in, QObject *target, const char *slot) +{ + in.invoke(target, slot); +} + +template<class Result> +Result invoke(QObject *target, const char *slot) +{ + InvokerBase in; + return invokeHelper<Result>(in, target, slot); +} + +template<class Result, class T0> +Result invoke(QObject *target, const char *slot, const T0 &t0) +{ + InvokerBase in; + in.addArgument(t0); + return invokeHelper<Result>(in, target, slot); +} + +template<class Result, class T0, class T1> +Result invoke(QObject *target, const char *slot, const T0 &t0, const T1 &t1) +{ + InvokerBase in; + in.addArgument(t0); + in.addArgument(t1); + return invokeHelper<Result>(in, target, slot); +} + +template<class Result, class T0, class T1, class T2> +Result invoke(QObject *target, const char *slot, + const T0 &t0, const T1 &t1, const T2 &t2) +{ + InvokerBase in; + in.addArgument(t0); + in.addArgument(t1); + in.addArgument(t2); + return invokeHelper<Result>(in, target, slot); +} + +} // namespace ExtensionSystem + +#endif // EXTENSIONSYSTEM_INVOKER_H diff --git a/src/libs/extensionsystem/pluginmanager.cpp b/src/libs/extensionsystem/pluginmanager.cpp index 7373e57bf3f..a8be0a37e49 100644 --- a/src/libs/extensionsystem/pluginmanager.cpp +++ b/src/libs/extensionsystem/pluginmanager.cpp @@ -39,14 +39,15 @@ #include "iplugin.h" #include "plugincollection.h" -#include <QtCore/QMetaProperty> +#include <QtCore/QDateTime> #include <QtCore/QDir> +#include <QtCore/QMetaProperty> +#include <QtCore/QSettings> #include <QtCore/QTextStream> -#include <QtCore/QWriteLocker> #include <QtCore/QTime> -#include <QtCore/QDateTime> -#include <QtCore/QSettings> -#include <QtDebug> +#include <QtCore/QWriteLocker> +#include <QtCore/QtDebug> + #ifdef WITH_TESTS #include <QTest> #endif @@ -60,9 +61,10 @@ enum { debugLeaks = 0 }; /*! \namespace ExtensionSystem - \brief The ExtensionSystem namespace provides classes that belong to the core plugin system. + \brief The ExtensionSystem namespace provides classes that belong to the + core plugin system. - The basic extension system contains of the plugin manager and its supporting classes, + The basic extension system contains the plugin manager and its supporting classes, and the IPlugin interface that must be implemented by plugin providers. */ @@ -114,15 +116,83 @@ enum { debugLeaks = 0 }; be implemented and added to the object pool. The plugin that provides the extension point looks for implementations of the class / interface in the object pool. \code - // plugin A provides a "MimeTypeHandler" extension point + // Plugin A provides a "MimeTypeHandler" extension point // in plugin B: MyMimeTypeHandler *handler = new MyMimeTypeHandler(); ExtensionSystem::PluginManager::instance()->addObject(handler); - // in plugin A: + // In plugin A: QList<MimeTypeHandler *> mimeHandlers = ExtensionSystem::PluginManager::instance()->getObjects<MimeTypeHandler>(); \endcode + + The \c{ExtensionSystem::Invoker} class template provides "syntactic sugar" + for using "soft" extension points that may or may not be provided by an + object in the pool. This approach does neither require the "user" plugin being + linked against the "provider" plugin nor a common shared + header file. The exposed interface is implicitly given by the + invokable methods of the "provider" object in the object pool. + + The \c{ExtensionSystem::invoke} function template encapsulates + {ExtensionSystem::Invoker} construction for the common case where + the success of the call is not checked. + + \code + // In the "provide" plugin A: + namespace PluginA { + class SomeProvider : public QObject + { + Q_OBJECT + + public: + Q_INVOKABLE QString doit(const QString &msg, int n) { + { + qDebug() << "I AM DOING IT " << msg; + return QString::number(n); + } + }; + } // namespace PluginA + + + // In the "user" plugin B: + int someFuntionUsingPluginA() + { + using namespace ExtensionSystem; + + QObject *target = PluginManager::instance() + ->getObjectByClassName("PluginA::SomeProvider"); + + if (target) { + // Some random argument. + QString msg = "REALLY."; + + // Plain function call, no return value. + invoke<void>(target, "doit", msg, 2); + + // Plain function with no return value. + qDebug() << "Result: " << invoke<QString>(target, "doit", msg, 21); + + // Record success of function call with return value. + Invoker<QString> in1(target, "doit", msg, 21); + qDebug() << "Success: (expected)" << in1.wasSuccessful(); + + // Try to invoke a non-existing function. + Invoker<QString> in2(target, "doitWrong", msg, 22); + qDebug() << "Success (not expected):" << in2.wasSuccessful(); + + } else { + + // We have to cope with plugin A's absence. + } + }; + \endcode + + \bold Note: The type of the parameters passed to the \c{invoke()} calls + is deduced from the parameters themselves and must match the type of + the arguments of the called functions \e{exactly}. No conversion or even + integer promotions are applicable, so to invoke a function with a \c{long} + parameter explicitly use \c{long(43)} or such. + \bold Note: The object pool manipulating functions are thread-safe. */ @@ -1095,3 +1165,38 @@ void PluginManagerPrivate::profilingReport(const char *what, const PluginSpec *s } } } + +/*! + \fn void PluginManager::getObjectByName() + Retrieves one object with a given name from the object pool. + \sa addObject() +*/ + +QObject *PluginManager::getObjectByName(const QString &name) const +{ + QReadLocker lock(&m_lock); + QList<QObject *> all = allObjects(); + foreach (QObject *obj, all) { + if (obj->objectName() == name) + return obj; + } + return 0; +} + +/*! + \fn void PluginManager::getObjectByClassName() + Retrieves one object inheriting a class with a given name from the object pool. + \sa addObject() +*/ + +QObject *PluginManager::getObjectByClassName(const QString &className) const +{ + const QByteArray ba = className.toUtf8(); + QReadLocker lock(&m_lock); + QList<QObject *> all = allObjects(); + foreach (QObject *obj, all) { + if (obj->inherits(ba.constData())) + return obj; + } + return 0; +} diff --git a/src/libs/extensionsystem/pluginmanager.h b/src/libs/extensionsystem/pluginmanager.h index ee70ecb9bdd..8d62ee8b09c 100644 --- a/src/libs/extensionsystem/pluginmanager.h +++ b/src/libs/extensionsystem/pluginmanager.h @@ -38,8 +38,8 @@ #include <aggregation/aggregate.h> #include <QtCore/QObject> -#include <QtCore/QStringList> #include <QtCore/QReadWriteLock> +#include <QtCore/QStringList> QT_BEGIN_NAMESPACE class QTextStream; @@ -95,6 +95,9 @@ public: return result; } + QObject *getObjectByName(const QString &name) const; + QObject *getObjectByClassName(const QString &className) const; + // Plugin operations QList<PluginSpec *> loadQueue(); void loadPlugins(); -- GitLab