從0到1構建自己的插件系統–插件管理
之前的文章已經可以完整的寫完一個插件,在本章節中我們解決插件的管理問題
插件管理是可以讓各個插件調用的橋樑,達到連通各個插件的目的,插件編寫者無需考慮插件之間的依賴關係,所有的這一切都由插件管理類來解決。插件管理主要有兩個部分組成,插件管理類和模塊信息類。插件管理本身也可以認爲是一個插件,這個插件是需要依賴lib的插件,在這個特殊插件中我們需要解決不同類型插件(如C++,JAVA,C#寫的二次開發插件)
插件加載
插件接口類
爲了解決插件可以使用不同的語言實現的問題,我們通過接口的方式在不同的語言中繼承(這個方案在我的SWIG技術專欄中有解決辦法)
//IDynamicLibrary.h接口文件,實現插件的加載;
#define CLASS_LIST std::vector<ClassEntry *>
//這個模塊的也可以使用插件系統;
#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<ClassEntry *> (*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<T>是一個智能指針類;
Object<IDynamicLibrary> 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<long> 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<int> classListByID(long iid)
{
auto cls_list = _clisd_class_map[iid];
std::vector<int> 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<LangModuleType,ILanguageAdapter*> _adapter_map;
//接口,類註冊信息列表;
std::hashMap<long, CLASS_LIST> _clisd_class_map;
//當前的插件列表;
std::vector<Object<IDynamicLibrary>> _class_module_list;
};
至此,插件管理器的主要實現就已經完成了,後一章主要解決類對象的問題。
ps:文中所有的代碼都屬於僞代碼