生活随笔
收集整理的這篇文章主要介紹了
C++插件浅谈之二
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
一、插件架構初步介紹
??? 想到寫本博客,也沒想到更好的名字,目前就先命這個名吧。說到插件架構,或許大部分IT從業者都聽過或者某些牛人也自己實現過穩定高效的插件框架。目前有很多軟件以及庫都是基于插件架構,例如PS、我所在行業的GIS軟件如Arcgis、QGIS、還比如開源圖形引擎OGRE以及OSG,這些都是插件架構,通過插件架構來進行功能的擴展。那到底什么是插件架構呢?我的理解是系統運行時在需要某個功能的時候動態加載的模塊,插件通常用動態鏈接庫實現,當然也可以用靜態庫,例如一些嵌入式系統中,比如IOS據說就不支持動態鏈接庫。
我們為什么要用插件架構呢?
??? 現代軟件工程已經從原先的通用程序庫逐步過渡到應用程序框架,比如一些C++的庫,這些庫都是實現某一領域特定功能的,比如GDAL,實現各種空間數據格式的解析,這種庫通常不是基于插件架構;應用程序框架比如JAVA里面的三大框架。首先,假設一個場景,以C++開發應用程序為例,我們的架構是基于APP+DLL的傳統架構,所有的功能糅雜在一起,這樣隨著系統的日益龐大,各種模塊之間耦合在一起,當修改其中一個模塊時,其他模塊也跟著一起受到影響,假如這兩個模塊式不同的開發人員負責的,就需要事先溝通好,這樣就造成了修改維護的困難。那怎么解決這個問題,插件架構是一種選擇。那么插件架構究竟有哪些好處呢?
1、方便功能的擴展。比如在GIS引擎設計中,一般的做法是不把數據格式的解析放在GIS內核中,只是在內核中定義一些通用的數據加載解析的接口,然后通過插件來實現某一特定格式的解析,這樣就可以擴展各種不同的數據格式,也方便移植。
2、更新量小。當底層的接口不變時,以插件形式存在的功能很容易獨立于應用程序而更新,只需要引入新版本的插件即可。相比發布整個應用程序,這種方式的更新量小很多。
3、降低模塊之間依賴,可以支持并行開發。比如兩個開發人員開發不同功能的插件,他們就可以只關心自己插件功能的實現,可以實現快速開發。
4、面向未來。當你的API到達一定穩定程度后,這時候你的API可能沒有更新的必要了。然而API的功能可以通過插件來進一步演化,這使得API可以再長時期內保持其可用性和適用性,使得你的API可以不被拋棄。
?
二、插件需要設計的東西
??? 這里我只考慮動態鏈接庫的情況。我們需要一種加載動態鏈接庫并訪問其中符號的機制。在一般的插件系統中,插件API和插件管理器是必須要設計的。
插件API。這個是創建插件必須要提供的接口,C++實現的話就是一個抽象類,里面只提供接口,具體功能交給插件實現。這部分代碼應該在你的核心API之內。
插件管理器。插件管理器負責插件的加載、注冊以及卸載等功能,管理插件的整個生命周期。該類一般都設計為單例模式,其實大部分資源管理的類一般都設計為單例模式。
插件和核心API之間的關系如下。
?
當我們把插件加載進來后,這時候還不知道插件怎么運行,為了讓插件正常的運行,這時候需要知道核心API應該訪問哪個具體的函數實現插件的正常運轉,定義的入口函數,這個可以通過導出標準C接口方式實現插件的初始化、停止等操作。
下面是具體的定義導出符號和標準C接口的實例。
[cpp]?view plain
?copy #ifdef?PLUGINCORE_EXPORTS????????? ????#ifdef?__GNUC__?? ????????????#define?PLUGINCORE_API?__attribute__((dllexport))?? ????????#else?? ????????????#define?PLUGINCORE_API?__declspec(dllexport)?? ????#endif?? ????#else?? ????????#ifdef?__GNUC__?? ????????????#define?PLUGINCORE_API?__attribute__((dllimport))?? ????????#else?? ????????????#define?PLUGINCORE_API?__declspec(dllimport)?? ????#endif?? #endif?? ?? extern?"C"?PLUGINCORE_API?PluginInstance?*StartPlugin();?? ?? extern?"C"?PLUGINCORE_API?void?StopPlugin();??
上面的StartPlugin就是動態庫加載進來時候需要訪問的符號,這個函數里面去啟動這個插件,StopPlugin是卸載插件時需要調用的函數。
這里用到了動態庫的導入,關于動態庫不同平臺上有不同的擴展名以及加載函數,為了保持API的跨平臺性,我這里簡單的封裝了動態庫加載和卸載的過程,用
typedef void* HLIB;表示動態庫的句柄。下面這個類也呈現給讀者,不妥的也給建議。
[cpp]?view plain
?copy #ifndef?DYNAMICLIB_INCLUDE?? #define?DYNAMICLIB_INCLUDE?? ?? ?? #include?"Export.h"?? ?? class?DynamicLib?? {?? public:?? ????DynamicLib(void);?? ????~DynamicLib(void);?? ?? ????const?char*?GetName()?const;?? ?? ?????? ????bool?LoadLib(const?char*?strLibName);?? ?? ????void*?GetSymbolAddress(const?char*?strSymbolName)?const;?? ?? ????void?FreeLib();?? ?? private:?? ????HLIB?m_hDynLib;??????? ????char*?m_pszLibName;??? };?? ?? #endif?? ?? #include?"DynamicLib.h"?? ?? DynamicLib::DynamicLib(void)?? {?? ????m_hDynLib?=?NULL;?? ????m_pszLibName?=?NULL;?? }?? ?? DynamicLib::~DynamicLib(void)?? {?? ????if?(m_hDynLib?!=?NULL)?? ????{?? ????????FreeLib();?? ????}?? ?? ????if?(m_pszLibName?!=?NULL)?? ????{?? ????????free(m_pszLibName);?? ????????m_pszLibName?=?NULL;?? ????}?? }?? ?? const?char*?DynamicLib::GetName()?const?? {?? ????return?m_pszLibName;?? }?? ?? #if?defined(__unix__)?||?defined(unix)?? #include?<dlfcn.h>?? ?? bool?DynamicLib::LoadLib(const?char*?strLibName)?? {?? ????std::string?strName?=?strLibName;?? ????strName?+=?".so";?? ????m_hDynLib?=?dlopen(strName.c_str(),?RTLD_LAZY);?? ????if(?pLibrary?==?NULL?)?? ????{?? ????????return?0;?? ????}?? ????m_pszLibName?=?strdup(strLibName);?? ?? ????return(?1?);?? }?? ?? void*?DynamicLib::GetSymbolAddress(const?char*?strSymbolName)?const?? {?? ????void????*pSymbol?=?NULL;?? ?? ????if?(m_hDynLib?!=?NULL)?? ????{?? ????????pSymbol?=?dlsym(m_hDynLib,strSymbolName);?? ????}?? ?? ????return?pSymbol;?? }?? ?? void?DynamicLib::FreeLib()?? {?? ????if?(m_hDynLib?!=?NULL)?? ????{?? ????????dlclose(m_hDynLib);?? ????????m_hDynLib?=?NULL;?? ????}?? ?? ????if?(m_pszLibName?!=?NULL)?? ????{?? ????????free(m_pszLibName);?? ????????m_pszLibName?=?NULL;?? ????}?? }?? ?? #endif?? ?? #ifdef?_WIN32?? #include?<Windows.h>?? ?? bool?DynamicLib::LoadLib(const?char*?strLibName)?? {?? ????std::string?strName?=?strLibName;?? ????strName?+=?".dll";?? ????m_hDynLib?=?LoadLibrary(strName.c_str());?? ????if?(m_hDynLib?!=?NULL)?? ????{?? ????????m_pszLibName?=?strdup(strLibName);?? ????????return?1;?? ????}?? ?? ????return?0;?? }?? ?? void*?DynamicLib::GetSymbolAddress(const?char*?strSymbolName)?const?? {?? ????if?(m_hDynLib?!=?NULL)?? ????{?? ????????return?(void*)GetProcAddress((HMODULE)m_hDynLib,strSymbolName);?? ????}?? ?? ????return?NULL;?? }?? ?? void?DynamicLib::FreeLib()?? {?? ????if?(m_hDynLib?!=?NULL)?? ????{?? ????????FreeLibrary((HMODULE)m_hDynLib);?? ????????m_hDynLib?=?NULL;?? ????}?? ?? ????if?(m_pszLibName?!=?NULL)?? ????{?? ????????free(m_pszLibName);?? ????????m_pszLibName?=?NULL;?? ????}?? }?? ?? #endif??
差點忘了,插件系統必須設計的插件API和插件管理器都還沒說,其實插件管理器是插件實例的集合,插件管理器提供了加載和卸載某一插件的功能,下面是插件API以及插件管理器的實例。
[cpp]?view plain
?copy #ifndef?PLUGININSTANCE_INCLUDE?? #define?PLUGININSTANCE_INCLUDE?? ?? ?? #include?"Export.h"?? ?? class?PLUGINCORE_API?PluginInstance?? {?? public:?? ????explicit?PluginInstance(const?std::string?&strName);?? ????virtual?~PluginInstance(void);?? ?? ????virtual?bool?Load()?=?0;?? ?? ????virtual?bool?UnLoad()?=?0;?? ?? ?????? ????virtual?std::string?GetFileName()?const?=?0;?? ?? ?????? ????virtual?std::string?GetDisplayName()?const?=?0;?? ?? private:?? ????PluginInstance(const?PluginInstance?&rhs);?? ????const?PluginInstance?&operator=(const?PluginInstance?&rhs);?? };?? ?? ?? typedef?PluginInstance?*(?*START_PLUGIN_FUN?)();?? typedef?void(?*STOP_PLUGIN_FUN?)();?? ?? #endif?? ?? ?? ?? #ifndef?PLUGINMANAGER_INCLUDE??? #define?PLUGINMANAGER_INCLUDE?? ?? #include?"Export.h"?? ?? class?PluginInstance;?? class?DynamicLib;?? ?? class?PLUGINCORE_API?PluginManager?? {?? public:?? ????static?PluginManager?&GetInstance();?? ?? ????bool?LoadAll();?? ?? ????PluginInstance*?Load(const?std::string?&strName,int?&errCode);?? ?? ????bool?LoadPlugin(PluginInstance?*pPlugin);?? ?? ????bool?UnLoadAll();?? ?? ????bool?UnLoad(const?std::string?&strName);?? ?? ????bool?UnLoadPlugin(PluginInstance?*pPlugin);?? ?? ????std::vector<PluginInstance?*>?GetAllPlugins();?? ?? private:?? ????PluginManager(void);?? ????~PluginManager(void);?? ????PluginManager(const?PluginManager?&rhs);?? ????const?PluginManager?&operator=(const?PluginManager?&rhs);?? ?? ????std::vector<PluginInstance?*>?m_vecPlugins;????? ????std::map<std::string,DynamicLib?*>?m_vecPluginLibs;????? };?? ?? #endif??
插件管理器可以通過系統的配置文件預先配置好加在哪些插件,一般可用XML配置。
有了上面的介紹之后,就該開始介紹整個插件加載和卸載的流程了,先來介紹怎么進行加載的。加載的函數式
PluginInstance* Load(const std::string &strName,int &errCode);
這個函數的功能是傳入一個不帶后綴的插件動態庫名字,如果插件管理器中沒有該插件就加載到系統中,并在插件列表中注冊,若存在的話就在插件列表中訪問該名字的插件,返回該插件實例。該函數的實現如下:
[cpp]?view plain
?copy PluginInstance*?PluginManager::Load(const?std::string?&strName,int?&errCode)?? {?? ????std::map<std::string,DynamicLib?*>::iterator?iter?=?m_vecPluginLibs.find(strName);?? ????if?(iter?==?m_vecPluginLibs.end())???? ????{?? ????????DynamicLib*?pLib?=?new?DynamicLib;?? ????????if?(pLib?!=?NULL)?? ????????{?? ????????????pLib->LoadLib(strName.c_str());?? ????????????m_vecPluginLibs.insert(make_pair(strName,pLib));?? ????????????START_PLUGIN_FUN?pFun?=?(START_PLUGIN_FUN)pLib->GetSymbolAddress("StartPlugin");?? ????????????if?(pFun?!=?NULL)?? ????????????{?? ????????????????PluginInstance*?pPlugin?=?pFun();?? ????????????????errCode?=?1;?? ?? ????????????????return?pPlugin;?? ????????????}?? ?? ????????????errCode?=?0;?? ????????????return?NULL;?? ????????}?? ????}?? ?? ????else?if?(iter?!=?m_vecPluginLibs.end())??????? ????{?? ????????for?(int?i?=?0;?i?<?m_vecPlugins.size();?i?++)?? ????????{?? ????????????if?(strName?==?m_vecPlugins[i]->GetDisplayName())?? ????????????{?? ????????????????errCode?=?1;?? ????????????????return?m_vecPlugins[i];?? ????????????}?? ????????}?? ????}?? ?? ????errCode?=?0;?? ????return?NULL;?? ?????? }??
從上面的過程可以看出,首先檢測插件是否存在,如果存在,就在插件列表中查找該插件直接返回該插件實例。如果不存在,就需要先創建一個DynamicLib* pLib = new DynamicLib;,然后通過pLib導入名字為strName的插件動態庫文件,再將這個模塊句柄和名字加入到模塊列表中,然后通過DynamicLib的GetSymbolAddress的函數獲得函數名為StartPlugin的函數指針,最后通過這個函數指針進行回調返回插件實例以及將該插件注冊到插件列表中,這個函數的在插件中的具體實現如下:
[cpp]?view plain
?copy ??
[cpp]?view plain
?copy static?PluginInstance?*pPlugin?=?NULL;?? ?? ?PluginInstance*?StartPlugin()?? ?{?? ?????pPlugin?=?new?ShapePlugin("shapefile");?? ?????PluginManager::GetInstance().LoadPlugin(pPlugin);?? ?? ?????return?pPlugin;?? ?}??
[cpp]?view plain
?copy ??
[cpp]?view plain
?copy ??
[cpp]?view plain
?copy bool?PluginManager::UnLoad(const?std::string?&strName)?? {?? ????std::map<std::string,DynamicLib?*>::iterator?iter?=?m_vecPluginLibs.begin();?? ????for?(;?iter?!=?m_vecPluginLibs.end();?++iter?)?? ????{?? ????????DynamicLib?*pLib?=?iter->second;?? ????????if?(NULL?==?pLib)?? ????????{?? ????????????continue;?? ????????}?? ????????if?(strcmp(pLib->GetName(),strName.c_str())?==?0)?? ????????{?? ????????????STOP_PLUGIN_FUN?pFun?=?(STOP_PLUGIN_FUN)pLib->GetSymbolAddress("StopPlugin");?? ????????????if?(pFun?!=?NULL)?? ????????????{?? ????????????????pFun();?? ????????????}?? ?? ????????????pLib->FreeLib();?? ????????????delete?pLib;?? ?? ?????????????? ????????????m_vecPluginLibs.erase(iter);?? ????????????return?true;?? ????????}?? ????}?? ?? ????return?false;?? }??
ShapePlugin就是繼承于PluginInstance的一個插件。
?
插件卸載的過程正好相反,下面也給出實現代碼。
[cpp]?view plain
?copy bool?PluginManager::UnLoad(const?std::string?&strName)?? {?? ????std::map<std::string,DynamicLib?*>::iterator?iter?=?m_vecPluginLibs.begin();?? ????for?(;?iter?!=?m_vecPluginLibs.end();?++iter?)?? ????{?? ????????DynamicLib?*pLib?=?iter->second;?? ????????if?(NULL?==?pLib)?? ????????{?? ????????????continue;?? ????????}?? ????????if?(strcmp(pLib->GetName(),strName.c_str())?==?0)?? ????????{?? ????????????STOP_PLUGIN_FUN?pFun?=?(STOP_PLUGIN_FUN)pLib->GetSymbolAddress("StopPlugin");?? ????????????if?(pFun?!=?NULL)?? ????????????{?? ????????????????pFun();?? ????????????}?? ?? ????????????pLib->FreeLib();?? ????????????delete?pLib;?? ?? ?????????????? ????????????m_vecPluginLibs.erase(iter);?? ????????????return?true;?? ????????}?? ????}?? ?? ????return?false;?? }??
這樣整個插件的流程就走通了,萬里長征第一步,現在只是實現了插件的注冊和調用,下面一部就要實現怎么去實現具體插件和怎么實現插件之間的通信了。
三、后記
本文主要是自己在探索C++插件實現機制的一些個人理解,雖然功能還很少,但是一些基本的東西還是有了。本插件實例的代碼的下載地址為:http://download.csdn.net/detail/zhouxuguang236/7466253?注意下載后請先看說明。
轉載自:??http://blog.csdn.net/zhouxuguang236/article/details/29365261
總結
以上是生活随笔為你收集整理的C++插件浅谈之二的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。