`
izuoyan
  • 浏览: 8931073 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

Qt plugin系统的几点说明

阅读更多

对于一个大型软件系统来说,实现 plugin 是一件很美妙的事情,一个成功的 plugin 系统可以使软件增色不少。 Plugin 最大的功能是在一定程度内提高了软件的灵活度和可扩展性。一个设计精良的 server 软件 plugin 系统甚至在 server 程序不退出的情况下可以调用新加入的 plugin ,实现不间断服务的升级。那么, Qt 是怎样实现它的 plugin 系统呢?

使用 Qt 创建 plugin 和在程序中调用 plugin 是很简单的事情, Qt 提供了很多 helper class 供大家使用。总体来说, Qt plugin 分为 2 种,按照 Qt 文档的说法,一种是高等级的 plugin 。其实说白了就是已经确定 interface Qt 本身的 plugin 。(大家可能都知道, Qt 的很多功能,像数据库驱动、图片格式支持、文字内码等都是通过 plugin 实现的)举个例子来说, Qt 可能本身没有西班牙语(希望这次西班牙夺冠 :- )的文字内码,但是程序员可以通过按照 codec interface 写出西班牙语的 codec plugin 从而使 Qt 支持西班牙语。

另一种是低等级的 plugin 。就是该 plugin interface 也需要程序员自己编写。所以如果你知道怎么写一个低等级的 plugin 并使用它之后,高等级的 plugin 也就完全掌握了。下面我就重点说说低等级的 plugin Qt 里实现的一些要点。

从编程的角度,重点还是 OOP 。所谓的 plugin ,其实就是一些按照特定 interface 写成的子类。该 Interface 必须是虚基类,且所有函数(除了析构)都是虚函数。而所谓的 plugin 就是继承该虚基类和 QObject 的子类。当程序调用该 plugin 的某个函数时,是通过该 plugin 的虚基类在运行时动态绑定至子类的 vtable 执行的。所以 Qt 实现 plugin 的基础还是 OOP 的继承和多态。

举个大家都知道的例子来说明, PhotoShop (可能它并不是这么实现的)的所有滤镜有个统一的 interface 虚基类,该类提供了一个虚函数 doSomeWork 用于实现滤镜效果。当用户选择某个特殊滤镜时,程序会调用 plugin 中该滤镜 class doSomeWork 实现函数来执行该操作,从而实现特定的滤镜功能。

那么为什么该 plugin 类不但要继承 interface 类,还需要继承 QObject 类呢?原因是调用 plugin 时需要该 plugin QObject 那部分的 meta 信息。如果大家看过例子代码,会发现,用 QPluginLoader 调用 plugin 的文件后,关键的一步是确定该 plugin 是什么类型的。简单的另人惊讶,一句 qobject_cast 就搞定了。刚看到这句我百思不得其解,好在 Qt 有源代码可看, 看了源代码发现 qobject_cast 类似于标准 C++ dynamic_cast ,且无需 RTTI 支持并能跨 DLL 。在代码中, qobject_cast 是通过 QObject metaobject cast 函数来实现的。那么该函数是怎么写的呢?

/*!

\internal

Returns \a obj if object \a obj inherits from this

meta-object; otherwise returns 0.

*/

QObject * QMetaObject :: cast( QObject * obj) const

{

if ( obj) {

const QMetaObject * m = obj-> metaObject();

do {

if ( m == this )

return const_cast < QObject *>( obj);

} while (( m = m-> d. superdata));

}

return 0 ;

}

从子类的 metaobject 开始查找,一直向父类循环,直至找到一致的 meta (类信息)。所以, qobject_cast 其实是确定该类是否是指定父类的派生类,如果是的话得到其指针。也就是说确定一个plugin是什么类型是通过继承关系来实现的。

从文件的角度来说,一个或多个 plugin 可以生存在同一个动态库里, windows 下是 dll 。那么,这个 dll 怎么能动态的将 plugin 暴露给调用者呢?最关键的是 Q_EXPORT_PLUGIN2 这个宏。 Qt 文档特意说明,同一个 plugin 必须且只能调用它一次,且必须在实现文件的末尾。为什么呢?

因为这个宏最终调用了另一个宏, Q_PLUGIN_INSTANCE ,而它的定义如下:

#define Q_PLUGIN_INSTANCE(IMPLEMENTATION) \

{ \

static QT_PREPEND_NAMESPACE(QPointer)<QT_PREPEND_NAMESPACE(QObject)> _instance; \

if (!_instance) \

_instance = new IMPLEMENTATION; \

return _instance; \

}

不知道大家有没有注意到 static 并且让 design pattern 中某个可爱的模式掠过你的脑海。从这个角度上说,你最适合在 plugin 中完成的代码是那些事务性的代码,即使用调用者的资源完成某些计算或操作。当你想在 plugin 中分配大量资源和组织一个庞大的类战队时,请三思。

其它的一些细节如 pro 文件怎么配置, plugin 文件的目录等等是一些细节问题,大家可以看文档解决。

不知我写的是不是太少且太跳跃了。我觉得大家水平都很高,一些简单的细节问题就不在费时费力描述了。只把自己认为有点意思和重要的信息写出来。 TX 们有兴趣的话也可以写些东西互相交流,毕竟 Qt 的中文资料太少了。

最后,关于最近比较火爆的皇帝唐的事情,我也有一些想法。不过我这个博主要是技术文章,所以我只说一句和大家共勉。“诚信比金子更可贵

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics