從0到1構建自己的外掛系統--外掛管理
之前的文章已經可以完整的寫完一個外掛,在本章節中我們解決外掛的管理問題
外掛管理是可以讓各個外掛呼叫的橋樑,達到連通各個外掛的目的,外掛編寫者無需考慮外掛之間的依賴關係,所有的這一切都由外掛管理類來解決。外掛管理主要有兩個部分組成,外掛管理類和模組資訊類。外掛管理本身也可以認為是一個外掛,這個外掛是需要依賴lib的外掛,在這個特殊外掛中我們需要解決不同型別外掛(如C++,JAVA,C#寫的二次開發外掛)
外掛載入
外掛介面類
為了解決外掛可以使用不同的語言實現的問題,我們透過介面的方式在不同的語言中繼承(這個方案在我的SWIG技術專欄中有解決辦法)
//IDynamicLibrary。h介面檔案,實現外掛的載入;
#define CLASS_LIST std::vector
//這個模組的也可以使用外掛系統;
#ifdef SWIG
%feature(“director”) IDynamicLibrary;
#endif
class IDynamicLibrary : public IUnkown
{
REGISTER_INTERFACE(IDynamicLibrary)
public:
enum LangModuleType //之前在註冊類提到了一個標記問題,在這裡使用列舉標記不同的實現介面類;
{
LangInvalid,
LangCpp,
LangCSharp,
LangJava
};
//關鍵的兩個介面,透過檔名載入外掛,可以將檔案放在某個固定的資料夾下;
virtual void loadPlugin(const std::string &name) = 0;
//獲取所有的類的註冊資訊;
virtual CLASS_LIST classList() = 0;
//透過註冊類資訊建立類物件,對於C++而言,只要呼叫ClassEntry的函式指標CreateClassInstance即可;
virtual IUnkown* createClass(ClassEntry* class_entry)=0;
};
外掛載入實現
外掛的載入主要是解決動態庫的載入,dll內部函式查詢,以及類的註冊資訊處理問題
//cppdynamiclibray類,繼承介面IDynamicLibrary
//外掛類是一個外掛一個例項類物件;
class CppDynamicLibrary : public IDynamicLibrary
{
public:
//這個宏在之前的文章中沒有提及,主要是在宏中加了一個獲取標記的函式
ClASS_DECLARE2(CppDynamicLibrary, IDynamicLibrary, LangCpp)
virtual bool loadPlugin(const std::string &name) //載入外掛
{
int64 hmod = loadLibrary(name);
if (hmod == 0)
{
return false;
}
return _initClasses(hmod);
}
virtual CLASS_LIST classList()
{
return _class_list;
}
private:
//載入類資訊;
bool _initClasses(int64 module)
{
if (!module)
{
return false;
}
//這個函式指標的返回值也可以使用void*處理,當然也需要修改外掛實現的那塊定義;
typedef std::vector
//查詢dll的函式名;
initClass plug_initclasses = (initClass)procAddress(module, “initClasses”);
if (!plug_initclasses)
{
std::cout << “非外掛dll”; return false;
}
_class_list = plug_initclasses();//執行函式;
return true;
}
private:
CLASS_LIST _class_list;
};
支援其他語言編寫外掛的最佳化(不需要其他語言編寫外掛,可忽略)
根據我們前面所說,註冊類資訊由ClassEntry類來儲存,其中例項化類透過ClassEntry的CreateClass函式指標來建立,但是對於其他語言而言,可能沒有函式指標或者委託,那麼我們就不能用這個方式建立了,因此我們如果要支援其他語言的外掛類註冊,需要解決建立例項化類函式的多型問題,解決辦法是建立一個建立物件的基類,每種語言編寫一個語言外掛一種實現,並註冊到外掛管理器中。同時在ClassEntry註冊類中增加一個方法標記這個註冊類是那種語言實現的即可
#ifdef SWIG
%feature(“director”) ILanguageAdapter;
#endif
//這個介面類無需註冊,透過外掛管理器靜態註冊的方法來解決;
class ILanguageAdapter : public IUnkown
{
virtual IUnkown* createClass(ClassEntry* class_entry)=0;
};
//對於C++而言,非常簡單
class CppLanguageAdapter:public ILanguageAdapter
{
virtual IUnkown* createClass(ClassEntry* class_entry)
{
return class_entry->createClass()();//獲取函式指標並呼叫函式指標;
}
};
//其他語言在各種的語言中繼承ILanguageAdapter
外掛類註冊
//隨便一個cpp檔案中加上自己的註冊程式碼即可;
MODULE_BEGIN(PluginCore) \\函式開頭
DEFINE_CLASSENTRY(CppDynamicLibrary) \\註冊外掛;
MODEL_END() \\模組結束標記
notes:IDynamicLibrary是一個外掛一個物件(負責外掛資訊的儲存,呼叫),ILanguageAdapter是一種開發語言的外掛方式支援一個例項化物件,他們一般情況下是在同一個動態庫中實現的。
外掛管理
外掛管理主要完成的幾件事情
對載入的外掛進行管理
外掛的延遲載入與解除安裝(解除安裝可能並不是特別安全,如果外掛的類物件沒有及時釋放的話)
類資訊的收集以及建立類物件的核心函式
透過類介面獲取所有其是實現類的標記列表
查詢並建立類處物件(下篇文章<<類物件建立機制>>)
外掛管理實現
class DLLOUT PluginManager //這個類需要導,該類是一個單例類;
{
enum LoadType
{
LoadTypeNeed,
LoadTypeForce
};
private:
PluginManager()
{
//當前模組中有這個initClasses,可以直接載入,在這個模組的其他地方需要做函式的宣告;
_addClassInfo(initClasses());
//註冊建立類例項的adpater;
registerAdapter(new CppLanguageAdapter());
}
public:
static PluginManager* instance()
{
static PluginManager* _plugin_manager=null;
if(_plugin_manager==null)
{
_plugin_manager=new PluginManager();
}
return _plugin_manager;
}
bool loadPlugin(const std::string &file_name, LangModuleType module_type, LoadType load_type)
{
//這個定義的實現在後續文章<<類物件建立機制>>中會有說明,Object
Object
if (!dynamic_lib->loadPlugin(file_name))
{
return false;
}
auto class_list = dynamic_lib->classList();
_addClassInfo(class_list);
_class_module_list。push_back(dynamic_lib);
}
private:
//獲取類資訊之後再儲存成介面id和對於的實現類列表的方式,這樣在類建立時查詢類註冊資訊就會非常快;
void _addClassInfo(CLASS_LIST class_list)
{
//內部函式;
auto load_clisid_list=[&](std::vector
{
for(int i = 0; i < clsid_list。size(); ++i)
{
long id = clsid_list。at(i);
auto cls_entry_iter = _clisd_class_map。find(id);
if (cls_entry_iter == _clisd_class_map。end())
{
CLASS_LIST list_value;
list_value。push_back(*it);
_clisd_class_map[id] = list_value;
}
else
{
cls_entry_iter->second。push_back(*it);
}
}
};
for (auto it = class_list。begin(); it != class_list。end(); ++it)
{
ClassEntry *cls_entry = (*it);
//類的介面ID列表
auto clsid_list = cls_entry->clsidList();
load_clisid_list(clsid_list);
}
}
//透過介面獲取當前實現該介面的所有標記列表(透過標記可以找到不同的實現類)
std::vector
{
auto cls_list = _clisd_class_map[iid];
std::vector
for (auto it = cls_list。begin(); it != cls_list。end(); ++it)
{
flag_list。append((*it)->flag());
}
return flag_list;
}
IUnkown* createObject(long id,int class_flag)
{
//該實現後後面介紹;
}
void registerAdapter(LangModuleType language_type,ILanguageAdapter* adpater)
{
std::map[language_type]=adapter;
}
private:
//多語言外掛化物件用,如果沒這個需要,可以不做這個處理;
//其他語言ILanguageAdapter這個對應的實現類的獲取基本上可以透過反射獲取;
std::map
//介面,類註冊資訊列表;
std::hashMap
//當前的外掛列表;
std::vector
};
至此,外掛管理器的主要實現就已經完成了,後一章主要解決類物件的問題。
ps:文中所有的程式碼都屬於虛擬碼