從0到1構建自己的插件系統--插件管理

從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:文中所有的代碼都屬於僞代碼
喜歡我就關注我吧

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章