對於一個大型軟件系統來說,實現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的中文資料太少了。
最后,關於最近比較火爆的皇帝唐的事情,我也有一些想法。不過我這個博主要是技術文章,所以我只說一句和大家共勉。“誠信比金子更可貴”
http://blog.csdn.net/superjoel/article/details/5725209
