[prev in list] [next in list] [prev in thread] [next in thread]
List: kde-core-devel
Subject: plugin loading (was: kcmoduleinfo / Plugin linking problem)
From: Matthias Kretz <kretz () kde ! org>
Date: 2007-08-12 10:44:42
Message-ID: 200708121244.47020.kretz () kde ! org
[Download RAW message or body]
[Attachment #2 (multipart/mixed)]
Hi,
attached you'll find some code examples of the current way our KDE plugin code
works (please correct me if I'm wrong there) and some code how I think it
could look. There's no way to support one export macro per plugin class if we
want to use Q_EXPORT_PLUGIN2 so my suggestion uses one factory class per DSO
The K_PLUGIN_FACTORY{,_DEFINITION,_DECLARATION} macros are simple enough that
we might not want to provide them - but that's a detail.
If you wonder how one can implement the factory take a look at main.cpp
(run "qmake -project; qmake; make" to compile it)
While going over it I found one thing which I think we should change in any
case: the "const QStringList &args" parameter should be a "const QVariantList
&args" instead.
--
________________________________________________________
Matthias Kretz (Germany) <><
http://Vir.homelinux.org/
MatthiasKretz@gmx.net, kretz@kde.org,
Matthias.Kretz@urz.uni-heidelberg.de
["pluginloading.cpp" (text/x-c++src)]
// custom plugin interfaces
class MyPlugin : public QObject
{
...
};
class MyOtherPlugin : public QObject
{
...
};
// a) from a KService
void loadFromService(const KService &service)
{
// the following will call KLibFactory::create(0, "MyPlugin")
MyPlugin *p1 = KService::createInstance<MyPlugin>(service);
// the following will call KLibFactory::create(0, "MyOtherPlugin")
MyOtherPlugin *p2 = KService::createInstance<MyOtherPlugin>(service);
// both calls currently use the same entry symbol and the same KLibFactory object, so the only
// way for the DSO to provide both is to use
// K_EXPORT_COMPONENT_FACTORY(libname, KGenericFactory<K_TYPELIST_2(MyPlugin, MyOtherPlugin)>(...))
// or create a custom KLibFactory
// with current kdelibs there's no way to get two different plugins of the same interface from
// the same DSO using KService::createInstance
}
// b) given the libname of the DSO
void loadFromLibName(const QString &libname)
{
// the following will call KLibFactory::create(0, "MyPlugin")
MyPlugin *p1 = KLibLoader::createInstance<MyPlugin>(libname);
// the following will call KLibFactory::create(0, "MyOtherPlugin")
MyOtherPlugin *p2 = KLibLoader::createInstance<MyOtherPlugin>(libname);
}
// c)
void loadTwoPlugins(const KService &s1, const KService &s2)
{
// with current kdelibs this will only work if the plugins are in different DSOs
MyPlugin *p1 = KService::createInstance<MyPlugin>(s1);
MyPlugin *p2 = KService::createInstance<MyPlugin>(s2);
}
// d) loading two objects implementing MyPlugin from the same DSO
void loadTwoPlugins(const QString &libname, const QString &f1, const QString &f2)
{
KLibrary *library = KLibLoader::library(libname);
if (!library) {
return;
}
MyPlugin *p1 = 0;
MyPlugin *p2 = 0;
KLibFactory *factory = library->factory(f1.toLatin1());
if (factory) {
p1 = factory->create<MyPlugin>();
}
factory = library->factory(f2.toLatin1());
if (factory) {
p2 = factory->create<MyPlugin>();
}
}
/*
Problems with the current state
1. implementing the DSO for a) and b) requires
K_EXPORT_COMPONENT_FACTORY(libname, KGenericFactory<K_TYPELIST_2(MyPlugin, MyOtherPlugin)>(...))
which is IMHO too cryptic/magic API
2. the two plugins loaded with c) can not be in the same DSO
3. d) way too complicated
4. it's impossible to use Q_EXPORT_PLUGIN2 to provide more than one plugin from the same DSO
*/
// API suggestion:
// The DSO:
class PluginImpl1 : public MyPlugin
{
...
};
class PluginImpl2 : public MyPlugin
{
...
};
class OtherPluginImpl : public MyOtherPlugin
{
...
};
K_PLUGIN_FACTORY(MyPluginFactory,
K_REGISTER_PLUGIN(PluginImpl1, MyPlugin);
K_REGISTER_PLUGIN(OtherPluginImpl, MyOtherPlugin);
K_REGISTER_PLUGIN_WITH_KEYWORD(PluginImpl2, MyPlugin, "other");
)
Q_EXPORT_PLUGIN2(libname, MyPluginFactory);
//////////////////////////////////////////////////////////////////////////////
// a) from a KService
void loadFromService(const KService &service)
{
MyPlugin *p1 = KService::createInstance<MyPlugin>(service);
MyOtherPlugin *p2 = KService::createInstance<MyOtherPlugin>(service);
Q_ASSERT(qobject_cast<PluginImpl1 *>(p1));
Q_ASSERT(qobject_cast<OtherPluginImpl *>(p2));
}
// b) given the libname of the DSO
void loadFromLibName(const QString &libname)
{
MyPlugin *p1 = KLibLoader::createInstance<MyPlugin>(libname);
MyOtherPlugin *p2 = KLibLoader::createInstance<MyOtherPlugin>(libname);
Q_ASSERT(qobject_cast<PluginImpl1 *>(p1));
Q_ASSERT(qobject_cast<OtherPluginImpl *>(p2));
}
// c)
void loadTwoPlugins(const KService &s1, const KService &s2)
{
// assume s1->library() == s2->library();
// KService::pluginKeyword() would read X-KDE-PluginKeyword
// s1->pluginKeyword() == QString()
// s2->pluginKeyword() == "other"
MyPlugin *p1 = KService::createInstance<MyPlugin>(s1);
MyPlugin *p2 = KService::createInstance<MyPlugin>(s2);
Q_ASSERT(qobject_cast<PluginImpl1 *>(p1));
Q_ASSERT(qobject_cast<PluginImpl2 *>(p2));
}
// d)
void loadPlugins(const QString &libname, const QString &key1, const QString &key2)
{
KLibFactory *factory = KLibLoader::factory(f1.toLatin1());
if (factory) {
MyPlugin *p1 = factory->create<MyPlugin>(key1);
MyPlugin *p2 = factory->create<MyPlugin>(key2);
}
}
///////////////////////////////////////////////////////////////////////////////
// if the DSO needs the KComponentData object of the Factory:
// myfactory.h
#include <kpluginfactory.h>
K_PLUGIN_FACTORY_DECLARATION(MyPluginFactory)
// myfactory.cpp
#include "myfactory.h"
#include "pluginimpl1.h"
#include "pluginimpl2.h"
#include "otherpluginimpl.h"
K_PLUGIN_FACTORY_DEFINITION(MyPluginFactory,
K_REGISTER_PLUGIN(PluginImpl1, MyPlugin);
K_REGISTER_PLUGIN(OtherPluginImpl, MyOtherPlugin);
K_REGISTER_PLUGIN_WITH_KEYWORD(PluginImpl2, MyPlugin, "other");
)
Q_EXPORT_PLUGIN2(libname, MyPluginFactory);
// e.g. pluginimpl1.cpp
#include "myfactory.h"
...
KComponentData kcd = MyPluginFactory::componentData();
...
["main.cpp" (text/x-c++src)]
#include <QtCore/QObject>
#include <QtCore/QVariant>
#include <QtCore/QtDebug>
#include <QtCore/QtPlugin>
#include <QtCore/QString>
#include <QtCore/QHash>
#include <QtCore/QByteArray>
#define K_REGISTER_PLUGIN_WITH_KEYWORD(impl, iface, keyword) \
do { \
struct impl##Factory : public SubFactory \
{ \
QObject *createInstance(QObject *parent, const QVariantList &args) { return \
new impl(parent, args); } \ }; \
registerPlugin(#iface, new impl##Factory, keyword); \
} while (false)
#define K_REGISTER_PLUGIN(impl, iface) K_REGISTER_PLUGIN_WITH_KEYWORD(impl, iface, \
QString()) #define K_PLUGIN_FACTORY_DECLARATION(name) \
class name : public FactoryBase \
{ \
public: \
name(); \
};
#define K_PLUGIN_FACTORY_DEFINITION(name, pluginRegistrations) \
name::name() \
{ \
pluginRegistrations \
}
#define K_PLUGIN_FACTORY(name, pluginRegistrations) \
K_PLUGIN_FACTORY_DECLARATION(name) \
K_PLUGIN_FACTORY_DEFINITION(name, pluginRegistrations)
class FactoryBase : public QObject
{
Q_OBJECT
public:
virtual ~FactoryBase()
{
typedef QHash<QString, SubFactory *> SubHash;
foreach (SubHash x, m_createInstanceHash) {
qDeleteAll(x);
}
}
template<typename T>
T *create(QObject *parent, const QVariantList &args = QVariantList())
{
return qobject_cast<T *>(createObject(T::staticMetaObject.className(), \
parent, args, QString())); }
template<typename T>
T *create(const QString &keyword, QObject *parent, const QVariantList &args = \
QVariantList()) {
return qobject_cast<T *>(createObject(T::staticMetaObject.className(), \
parent, args, keyword)); }
protected:
struct SubFactory
{
virtual ~SubFactory() {}
virtual QObject *createInstance(QObject *parent, const QVariantList \
&args) = 0; };
void registerPlugin(const QByteArray &iface, SubFactory *subFactory, const \
QString &keyword) {
if (m_createInstanceHash[iface].contains(keyword)) {
qDebug() << "Warning: overwriting plugin with interface" << iface << \
"and keyword" << keyword; }
m_createInstanceHash[iface][keyword] = subFactory;
}
virtual QObject *createObject(const char *iface, QObject *parent, const \
QVariantList &args, const QString &keyword) {
SubFactory *subFactory = m_createInstanceHash[iface].value(keyword);
if (subFactory) {
return subFactory->createInstance(parent, args);
}
return 0;
}
private:
QHash<QByteArray, QHash<QString, SubFactory *> > m_createInstanceHash;
};
/* --------------------------------------------------------------------------------------------- \
*/
class MyPlugin : public QObject
{
Q_OBJECT
public:
MyPlugin(QObject *parent, const QVariantList &args) : QObject(parent) {}
};
class MyOtherPlugin : public QObject
{
Q_OBJECT
public:
MyOtherPlugin(QObject *parent, const QVariantList &args) : QObject(parent) {}
};
class A : public MyPlugin
{
Q_OBJECT
public:
A(QObject *parent, const QVariantList &args) : MyPlugin(parent, args) {}
};
class B : public MyPlugin
{
Q_OBJECT
public:
B(QObject *parent, const QVariantList &args) : MyPlugin(parent, args) {}
};
class C : public MyOtherPlugin
{
Q_OBJECT
public:
C(QObject *parent, const QVariantList &args) : MyOtherPlugin(parent, args) {}
};
K_PLUGIN_FACTORY(Factory,
K_REGISTER_PLUGIN(A, MyPlugin);
K_REGISTER_PLUGIN(C, MyOtherPlugin);
K_REGISTER_PLUGIN_WITH_KEYWORD(A, MyPlugin, "foo");
K_REGISTER_PLUGIN_WITH_KEYWORD(B, MyPlugin, "bar");
)
Q_EXPORT_PLUGIN2(libname, Factory)
int main()
{
FactoryBase *f = qobject_cast<FactoryBase *>(qt_plugin_instance());
Q_ASSERT(f);
QObject parent;
MyPlugin *a1 = f->create<MyPlugin>(&parent);
MyPlugin *a2 = f->create<MyPlugin>("foo", &parent);
MyPlugin *b = f->create<MyPlugin>("bar", &parent);
MyOtherPlugin *c = f->create<MyOtherPlugin>(&parent);
Q_ASSERT(a1);
Q_ASSERT(qobject_cast<A *>(a1));
Q_ASSERT(a2);
Q_ASSERT(qobject_cast<A *>(a2));
Q_ASSERT(b);
Q_ASSERT(qobject_cast<B *>(b));
Q_ASSERT(c);
Q_ASSERT(qobject_cast<C *>(c));
delete f;
return 0;
}
#include "main.moc"
[Attachment #7 (application/pgp-signature)]
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic