VC里面實現(xiàn)動態(tài)對象載入已經(jīng)不是什么新鮮事情了,很多的plug-in技術(shù)就是例子。Unix下,通過動態(tài)載入so獲得一個對象也不是什么難事,不過對這個對象的管理就是一件比較麻煩的事情了。一般的需求如下:
??有class TMyObj,準確說TMyObj應(yīng)該是一個接口,根據(jù)不同具體情況會有不同的實現(xiàn),例如 TMyObj1、TMyObj2等等……而這些TMyObj1和TMyObj2分別保存在不同的so當(dāng)中,需要根據(jù)不同的時候load不同的so,建立相應(yīng)的對象。由于這些對象都擁有TMyObj的接口,所以對于外部來說對這些類的使用就像對TMyObj的使用一樣。
??看起來好像比較簡單,只要在so里面引出一個函數(shù):
??TMyObj * onCreateObject(void);
??而函數(shù)在so中的具體實現(xiàn)就是建立不同的子類,例如在obj1.so中:
??TMyObj * onCreateObject(void)
???{?return new TMyObj1;?}
??使用的時候只需要動態(tài)load入obj1.so,并且找到onCreateObject函數(shù)的入口,就可以建立一個具有TMyObj接口的TMyObj1了。
??至于釋放對象,一般有兩種方法:
方法一:
??so中包含另外一個函數(shù):
??void onDestroyObj(void * p)
??{
????TMyObj1 * tp = (TMyObj1 *)p;
????delete tp;
??}
??從so中導(dǎo)出該函數(shù),并在刪除對象的時候調(diào)用。
方法二:
??TMyObj的析構(gòu)函數(shù)聲明為虛函數(shù),那么從so導(dǎo)出的onCreateObject()建立的對象,直接執(zhí)行delete刪除就行了,由于析構(gòu)函數(shù)是虛函數(shù),編譯器會正確的調(diào)用TMyObj1的析構(gòu)函數(shù)。
??當(dāng)然,方法二是比較簡單而優(yōu)雅的方法,既然對于C++來說接口就相當(dāng)于純虛函數(shù),多增加一個析構(gòu)的虛函數(shù)又何妨呢。但是無論使用哪種方法,都要注意一個問題,就是載入的obj1.so的生命周期要比最后一個TMyObj1的生存周期長。即只要內(nèi)存中還存在TMyObj1對象,obj1.so就要一直在內(nèi)存中,不能卸載。要保證這個同步,是比較麻煩的事情。下面就說說我的解決方法:
??
??首先,要選擇一個通用的載入so的lib,這個可以參考一下common c++的DSO(在file.h)里面。(不想使用common c++?我也只是說“參考”而已)。這個支持DLL和so,通過成員函數(shù)void *operator[](const char *);獲得指定的symbol的入口。
??其次,就要選擇一個通用的SmartPtr。這個當(dāng)然Loki是首選,Loki的SmartPtr的靈活性比boost的smart_ptr強多了,而且Loki也小巧的多。
??然后就要實現(xiàn)一個簡單的so的manager,其實應(yīng)該說是一個動態(tài)object的factory:
??class TObjFactory : protected DSO
??{
??public:
????TObjFactory(void);
????
????void?load(const std::string & strPath);
????void *?createObj(void) const throw (TSOException);
??protected:
????typedef void * (*funcCreate)(void ** p);
????funcCreate??m_pCreator;
??};
??可以想象這個類干些什么:load就是載入相應(yīng)的so,然后獲得so中onCreateObject函數(shù)的入口,并賦給成員m_pCreator。而createObj就是調(diào)用m_pCreator建立對象。不過有所不同的是 m_pCreator所指向的函數(shù)形式是void * funcCreate(void ** p),而多出來void **p用處就是可以讓so中的構(gòu)造函數(shù)中產(chǎn)生的exception能夠傳遞出來。這個不能說不是so的麻煩之處,so中函數(shù)的exception不能被外部捕獲,所以只好這樣子做了。
??現(xiàn)在,關(guān)鍵的地方來了,就是要保證這個TObjFactory的生存周期了。選擇Loki的SmartPtr就能派上用場了。
??Loki的SmartPtr可以自己選定適用的StoragePolicy,這正是我們需要的,參考DefaultSPStorage,可以做我們的TMySOStoragePolicy:
??template
???class TMySOStoragePolicy
???{
????.....
???protected:
????void Destroy()
??????? {
??????? ?delete pointee_;
??????? ?m_pFactory = SmartPtr();
??????? }???
???private:
????SmartPtr?m_pFactory;
????StoredType ??????pointee_;
???};
??顯而易見,這樣做的目的就是要保證釋放指針的時候就減少TObjFactory的引用計數(shù)。
??好了,現(xiàn)在就是主角了:
??template
??class TDObj : public SmartPtr
??{
??public:
????TDObj(void);
????TDObj(const TDObj & obj);
????.....
????
??protected:
????friend class TDObjManager;
????TDObj(T * p, SmartPtr pManager);
??};
??
??class TDObjManager
??{
??public:
????......
????template
?????static TDObj??createObj(const std::string & strKeyName)
?????{
???????SmartPtr pFactory = getFactoryByName(strKeyName);
???????//這里面可以做很多事情了,例如訪問內(nèi)存,查找相應(yīng)的Factory;或者讀取配置文件、讀入新的so并建立新的Factory。
???????//或者根據(jù)一些淘汰算法,先淘汰內(nèi)存的Factory,然后重新載入新的Factory等等。
???????std::auto_ptr _au( static_cast(pFactory->createObj()) );
???????return TDObj( _au.release(), pFactory);
?????}
??};
??
??以后用起來就簡單多了:
??class TMyObj
??{
??public:
???virtual ~TMyObj(void);
???virtual int func(void) = 0;
??};
??
??TDObj?obj1 = TDObjManager::createObj( "obj1.so") );
??TDObj?obj2 = TDObjManager::createObj( "obj2.so") );
??
??cout << obj1->func() << endl;
??cout << obj2->func() << endl;
??說了這么久,都是主程序的調(diào)用,而so中應(yīng)該如何呢?其實也很簡單:
??class TMyObj1 : public TMyObj
??{
??public:
????TMyObj1(void);
????~TMyObj1(void);
????static void?onStaticInit(void);
????static void?onStaticDestroy(void);
????static const char *?getVersion(void);
????static const char *?getObjectName(void);
????
????virtual int??func(void);
??};
??
??DECLARE_SO_INTERFACE(TMyObj1);
??
??DECLARE_SO_INTERFACE其實是一個為了方便編寫程序而定義的宏:
??#define DECLARE_SO_INTERFACE(x)?extern "C" {?\
????void onInstallDLL(void);???\
????void onUninstallDLL(void);???\
????const char * onGetVersion(void);?\
????const char * onObjectName(void);?\
????void *?onCreateObject(void ** ppException);??\
???}; \
???void?onInstallDLL(void)?{ x::onStaticInit(); }????\
???void?onUninstallDLL(void)?{ x::onStaticDestroy(); }??\
???const char * onGetVersion(void) { return x::getVersion(); }??\
???const char * onObjectName(void)?{ return x::getObjectName(); }?\
???void *?onCreateObject(void ** pException)?{?\
????try { \
?????*pException = NULL; x * p = new x();?return (void *)p; \
????}catch(std::exception & e) { \
?????*pException = new std::exception(e); \
?????return NULL; ?\
????}?\
???}
???
??可以看到除了導(dǎo)出onCreateObject函數(shù)以外,還導(dǎo)出了:
??TMyObj1::onStaticInit用于載入so的時候執(zhí)行初始化操作;
??TMyObj1::onStaticDestroy用于卸載so的時候執(zhí)行清理操作;
??TMyObj1::getVersion?獲得對象的版本信息
??TMyObj1::onObjectName 獲得對象名信息等
??可以擴展前面的TObjFactory,實現(xiàn)這些功能。
??同理,我們可以做obj2.so:
??class TMyObj2 : public TMyObj
??{
??public:
???TMyObj2(void);
???~TMyObj2(void);
??
???static void?onStaticInit(void);
???static void?onStaticDestroy(void);
???static const char *?getVersion(void);
???static const char *?getObjectName(void);
??
???virtual int??func(void);
??};
??
??DECLARE_SO_INTERFACE(TMyObj2);
??
??
??另外,一個值得討論的問題是:C++由于沒有反射機制,所以無法實現(xiàn)設(shè)值注入和構(gòu)造注入,只能實現(xiàn)接口注入。不過一般來說也已經(jīng)足夠使用了。