• 5431阅读
  • 1回复

[共享]Creating Plugins (二) [复制链接]

上一主题 下一主题
离线escene
 

只看楼主 倒序阅读 楼主  发表于: 2011-05-15
接前面的Qt插件学习(一),继续学习插件的 lower level api这次,直接写个小例子吧:接口
程序要能感知插件,需要程序和插件共同遵守某种规则。于是定义一个共同的接口[pre]//mathinterface.h
#include <QtCore/QtPlugin>
class MathInterface
{
public:
    virtual ~MathInterface() {}
    virtual int math(int v) = 0;
};
Q_DECLARE_INTERFACE(MathInterface, "com.example.Plugin.MathInterface/0.1");[/pre]这儿用到的这个宏的定义在qobject.h中:[pre]#define Q_DECLARE_INTERFACE(IFace, IId) \
    template <> inline const char *qobject_interface_iid<IFace *>() \
    { return IId; } \
    template <> inline IFace *qobject_cast<IFace *>(QObject *object) \
    { return reinterpret_cast<IFace *>((object ? object->qt_metacast(IId) : 0)); } \
    template <> inline IFace *qobject_cast<IFace *>(const QObject *object) \
    { return reinterpret_cast<IFace *>((object ? const_cast<QObject *>(object)->qt_metacast(IId) : 0)); }[/pre]注意,这儿用到了 [pre]qt_metacast()[/pre]稍后会看到这个函数在何处被定义。
使用插件

[pre]//main.cpp
#include <QtCore>
#include "mathinterface.h"

int main(int argv, char *args[])
{
    QCoreApplication app(argv, args);
    QDir pluginsDir(qApp->applicationDirPath());
    foreach (QString fileName, pluginsDir.entryList(QDir::Files)) {
        QPluginLoader pluginLoader(pluginsDir.absoluteFilePath(fileName));
        QObject *plugin = pluginLoader.instance();
        if (plugin) {
            MathInterface *interface = qobject_cast<MathInterface *>(plugin);
            if (interface) {
                qDebug()<<interface->math(10);
            }
        }
    }
    return app.exec();
}[/pre]遍历一个路径(比如这儿的可执行程序所在目录),依次用 QPluginLoader 进行加载,如果加载成功且转换成接口指针成功,则调用接口的函数。
这儿用了一个 qobject_cast 进行类型转换。是不是很神奇:这儿转换的类型不是我们一直坚信的 QObject 或其子类。这用的是我们接口定义时的宏。
插件定义
头文件
[pre]//plugin1.h
#include <QtCore/QObject>
#include "mathinterface.h"

class Plugin1:public QObject, public MathInterface
{
    Q_OBJECT
    Q_INTERFACES(MathInterface)
public:
    Plugin1(QObject *parent=NULL);
    int math(int v);
};[/pre]cpp文件
[pre]//plugin1.cpp
#include "plugin1.h"
Plugin1::Plugin1(QObject *parent)
    :QObject(parent)
{
}
int Plugin1::math(int v)
{
    return 100*v;
}
Q_EXPORT_PLUGIN2(plugin1, Plugin1);[/pre]这儿出现的新的宏是:Q_INTERFACES(MathInterface)
这是个moc处理的宏,moc的结果中包含下面的代码:
[pre]void *Plugin1::qt_metacast(const char *_clname)
{
    if (!_clname) return 0;
    if (!strcmp(_clname, qt_meta_stringdata_Plugin1))
        return static_cast<void*>(const_cast< Plugin1*>(this));
    if (!strcmp(_clname, "MathInterface"))
        return static_cast< MathInterface*>(const_cast< Plugin1*>(this));
    if (!strcmp(_clname, "com.example.Plugin.MathInterface/0.1"))
        return static_cast< MathInterface*>(const_cast< Plugin1*>(this));
    return QObject::qt_metacast(_clname);
}[/pre]从这儿可以看出前面的qobject_cast是如何通过调用qt_metacast来起作用的。
pro文件
为了完整,看一下程序和插件分别对应的pro文件
  • project.pro
  • app/
    • app.pro
    • main.cpp
    • mathinterface.h
  • plugin1/
    • plugin1.pro
    • plugin1.h
    • plugin1.cpp

[pre]#app.pro
HEADERS    = mathinterface.h
SOURCES    = main.cpp
CONFIG += console
TARGET     = main
DESTDIR    = ../[/pre]
[pre]#plugin1.pro
TEMPLATE        = lib
CONFIG         += plugin
INCLUDEPATH    += ../app
HEADERS         = plugin1.h
SOURCES         = plugin1.cpp
DESTDIR         = ../[/pre]其实Qt自带的Manual和例子讲得很清楚了,本文中不过提了一点用得到宏是如何工作的。
QPluginLoader
使用插件是通过QPluginLoader来完成的。
回想上一篇中提到 Q_EXPORT_PLUGIN2(PluginName, ClassName) 展开后的两个函数:
[pre]static const char qt_plugin_verification_data[] = \
      "pattern=QT_PLUGIN_VERIFICATION_DATA\n" \
      "version=4.7.0\ndebug=false\nbuildkey=xxx";

extern "C" Q_DECL_EXPORT const char * qt_plugin_query_verification_data()
{
     return qt_plugin_verification_data;
}

extern "C" Q_DECL_EXPORT qt_plugin_instance()
{
     static QPointer<QObject> _instance;
     if (!_instance)
     _instance = new ClassName;
     return _instance;
}[/pre]QPluginLoader 正是借助这两个接口函数,来判断我们的插件是否有效的。看一下load的源码:
这儿的d是 QLibraryPrivate 的实例

[pre]bool QPluginLoader::load()
{
    if (!d || d->fileName.isEmpty())
        return false;
    if (did_load)
        return d->pHnd && d->instance;
    if (!d->isPlugin())
        return false;
    did_load = true;
    return d->loadPlugin();
}
[/pre]两个接口函数是分别在 d->isPlugin() 和 d->loadPlugin() 中被使用的。
参考

离线ppdayz

只看该作者 1楼 发表于: 2011-05-15
楼主可以把原帖地址贴出来的,转载欢迎,但是个人觉得还是要尊重下原作者(不是我。。。)
快速回复
限100 字节
 
上一个 下一个