從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 (*initClass)();

//查詢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 dynamic_lib(module_type);

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 clisd_list)

{

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 classListByID(long iid)

{

auto cls_list = _clisd_class_map[iid];

std::vector flag_list;

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 _adapter_map;

//介面,類註冊資訊列表;

std::hashMap _clisd_class_map;

//當前的外掛列表;

std::vector> _class_module_list;

};

至此,外掛管理器的主要實現就已經完成了,後一章主要解決類物件的問題。

ps:文中所有的程式碼都屬於虛擬碼