[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