嚴(yán)以律己,寬以待人. 三思而后行. GMail/GTalk: yanglinbo#google.com; MSN/Email: tx7do#yahoo.com.cn; QQ: 3 0 3 3 9 6 9 2 0 .
文章來源:http://masterdog.bokee.com/563395.html??????應(yīng)用程序中使用插件技術(shù),有利于日后的版本更新、維護(hù)(比如打補(bǔ)?。┖凸δ軘U(kuò)展,是一種很實(shí)用的技術(shù)。其最大的特點(diǎn)是更新插件時無需重新編譯主程序,對于一個設(shè)計良好的應(yīng)用系統(tǒng)而言,甚至可以做到業(yè)務(wù)功能的在線升級。本文介紹了linux下用C++實(shí)現(xiàn)插件的一個簡單實(shí)例,希望能對大家有所啟發(fā)。
為了能做到更新插件時無需重新編譯主程序,要求主程序中定義的接口是定死的,而接口的實(shí)現(xiàn)被放到了具體的插件中,這樣主程序在運(yùn)行時刻將插件加載進(jìn)來,就可以使用這些接口所提供的功能了。在面向?qū)ο蟮南到y(tǒng)中,各個功能模塊被封裝到類中,因此在C++中實(shí)現(xiàn)插件技術(shù),就需要在主程序中提供基類,并為這些基類定義明確的接口,然后在插件(動態(tài)庫或共享庫)中定義派生類,并實(shí)現(xiàn)基類中所有的接口。
我們以計算多邊形面積為例,首先定義一個基類CPolygon:
注意基類不一定是虛類(有純虛函數(shù)的類),但是接口一定要定義成虛函數(shù),因?yàn)樽罱K主程序是通過基類指針來調(diào)派生類的接口函數(shù),另外如果基類中有資源分配(new)的話,析構(gòu)函數(shù)一定要定義成虛的,否則不會被調(diào)用,造成內(nèi)存泄漏。
接下來要定義派生類,并放到共享庫(triangle.so)中:
其中定義了函數(shù)“create”用來創(chuàng)建CTriangle類對象,該函數(shù)可讓主程序獲得CTriangle對象指針,從而可以訪問CTriangle類對象。主程序通過調(diào)用dlsym獲取指向該函數(shù)的指針,需要指出的是,由于dlsym被設(shè)計成c-style方式,因此調(diào)用c++定義的函數(shù)時,需要加上extern "C"
那么主程序是如何調(diào)用共享庫的呢,代碼片段如下:
主程序通過dlopen打開triangle.so,然后通過dlsym得到庫中的函數(shù)create指針,調(diào)用create后返回了指向CTriangle類對象的指針,類型是CPolygon的,由于虛函數(shù)的多態(tài)性, pObj->area() 實(shí)際是調(diào)用了CTriangle::area.
好了,插件技術(shù)就是這么簡單,回顧一下實(shí)現(xiàn)過程:寫一個基類,定義接口函數(shù),然后在共享庫中寫派生類,最后在主程序運(yùn)行時刻打開共享庫(dlopen),并通過create函數(shù)得到指向新創(chuàng)建的派生類對象的指針,然后利用虛函數(shù)的多態(tài)性,調(diào)用派生類的各種方法。
不過進(jìn)一步使用后你可能會發(fā)現(xiàn),這樣實(shí)現(xiàn)會有些問題:
1. 每寫一個派生類就需要重寫一個create函數(shù)
注意到CTriangle類實(shí)現(xiàn)時定義的create函數(shù)必須返回 new CTriangle:
?
那么如果再建一個類比如CRectangle, create函數(shù)必須重寫,返回 new CRectangle
這樣做一方面麻煩,另外CTriangle、CRectangle兩個類不能放到同一個共享庫中,否則會編譯時刻提示重復(fù)定義錯誤。
2. 主程序無法判斷create函數(shù)返回的是哪個類所創(chuàng)建的對象
當(dāng)只有一個基類(CPolygon)時主程序當(dāng)然知道返回的是CPolygon派生類的對象指針:create_t * create_triangle = (create_t *)dlsym(handle, "create");CPolygon * pObj = create_triangle();
假如有多個基類,根據(jù)這些基類派生出不同類型的類時,無法在主程序中判斷返回的是那個類的對象。
3. 操作繁瑣
沒有一個統(tǒng)一的操作界面,實(shí)現(xiàn)共享庫的加載、卸載、派生類對象的創(chuàng)建,特別是當(dāng)需要加載一個目錄下所有的共享庫時,感覺一個一個地加載太麻煩了,能不能批量加載呢。
通過動態(tài)類加載和建立Helper類可以很好地解決上述問題,其中dynclass.h/dynclass.cpp中實(shí)現(xiàn)了動態(tài)加載類對象,pluginhelper.h/pluginhelper.cpp實(shí)現(xiàn)了Plugin Helper,具體細(xì)節(jié)見附件。
下面簡單介紹一下使用步驟:
1. 首先定義基類(CPolygon),方法同上
2. 在共享庫中實(shí)現(xiàn)派生類
比如CTriangle:
注意到此時派生類的實(shí)現(xiàn)(triangle.cpp)中已沒有了那個討厭的create了,被我偷偷放到dynclass.cpp中了:
由于對任何派生類而言,該函數(shù)的實(shí)現(xiàn)都一樣,因此只需要實(shí)現(xiàn)一次,對使用者是不可見的,達(dá)到了從派生類中拿走的目的。
另外增加了一個宏:DYN_DECLARE(CTriangle); 參數(shù)是類名(這里用到了RTTI),每個派生類對應(yīng)一個這樣的宏,該類就可以支持類對象的動態(tài)加載了,需要包含頭文件dynclass.h
2. 在主程序中如何使用
使用起來也非常簡單,在主程序(main.cpp)中:
首先定義CPluginHelper對象,調(diào)用Load方法加載共享庫,其中第一個參數(shù)是共享庫的路徑,第二個參數(shù)是共享庫的名稱,共享庫名支持模式匹配,這里表示要加載./plugin目錄所有so共享庫,當(dāng)然也可以是某個具體的共享庫名。
隨后可以通過CPluginHelper::Create方法,根據(jù)類名稱創(chuàng)建該類的對象,實(shí)現(xiàn)了參數(shù)化創(chuàng)建對象的目的,然后就是對該對象的調(diào)用,當(dāng)不用該對象時,需要調(diào)用delete來刪除。
最后,調(diào)用CPluginHelper::Unload將指定共享庫卸載。
http://masterdog.bokee.com/inc/20050116132524159116.zip
posted on 2006-08-26 04:37 楊粼波 閱讀(825) 評論(0) 編輯 收藏 引用 所屬分類: C++
Powered by: C++博客 Copyright © 楊粼波