青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

清源游民 gameogre@gmail.com

本文討論一種簡單卻有效的插件體系結構,它使用C++,動態鏈接庫,基于面向對象編程的思想。
首先來看一下使用插件機制能給我們帶來哪些方面的好處,從而在適當時候合理的選擇使用。
1, 增強代碼的透明度與一致性:因為插件通常會封裝第三方類庫或是其他人編寫的代碼,需要清晰地定義出接口,用清晰一致的接口來面對所有事情。你的代碼也不會被轉換程序或是庫的特殊定制需求弄得亂七糟。
2, 改善工程的模塊化:你的代碼被清析地分成多個獨立的模塊,可以把它們安置在子工程中的文件組中。這種解耦處理使得創建出的組件更加容易重用。
3, 更短的編譯時間:如果僅僅是為了解釋某些類的聲明,而這些類內部使用了外部庫,編譯器不再需要解析外部庫的頭文件了,因為具體實現是以私有的形式完成。
4, 更換與增加組件:假如你需要向用戶發布補丁,那么更新單獨的插件而不是替代每一個安裝了的文件更為有效。當使用新的渲染器或是新的單元類型來擴展你的游戲時,能過向引擎提供一組插件,可以很容易的實現。
5, 在關閉源代碼的工程中使用GPL代碼:一般,假如你使用了GPL發布的代碼,那么你也需要開放你的源代碼。然而,如果把GPL組件封裝在插件中,你就不必發布插件的源碼。

介紹
先簡單解釋一下什么是插件系統以及它如何工作:在普通的程序中,假如你需要代碼執行一項特殊的任務,你有兩種選擇:要么你自己編寫,要么你尋找一個已經存在的滿足你需要的庫。現在,你的要求變了,那你只好重寫代碼或是尋找另一個不同的庫。無論是哪種方式,都會導致你框架代碼中的那些依賴外部庫的代碼重寫。
現在,我們可以有另外一種選擇:在插件系統中,工程中的任何組件不再束縛于一種特定的實現(像渲染器既可以基于OpenGL,也可以選擇Direct3D),它們會從框架代碼中剝離出來,通過特定的方法被放入動態鏈接庫之中。
所謂的特定方法包括在框架代碼中創建接口,這些接口使得框架與動態庫解耦。插件提供接口的實現。我們把插件與普通的動態鏈接庫區分開來是因為它們的加載方式不同:程序不會直接鏈接插件,而可能是在某些目錄下查找,如果發現便進行加載。所有插件都可以使用一種共同的方法與應用進行聯結。

常見的錯誤
一些程序員,當進行插件系統的設計時,可能會給每一個作為插件使用的動態庫添加一個如下函數類似的函數:PluginClass *createInstance(const char*);
然后它們讓插件去提供一些類的實現。引擎用期望的對象名對加載的插件逐個進行查詢,直到某個插件返回,這是典型的設計模式中“職責鏈”模式的做法。一些更聰明的程序員會做出新的設計,使插件在引擎中注冊自己,或是用定制的實現替代引擎內部缺省實現:
Void dllStartPlugin(PluginManager &pm);
Void dllStopPlugin(PluginManager &pm);
第一種設計的主要問題是:插件工廠創建的對象需要使用reinterpret_cast<>來進行轉換。通常,插件從共同基類(這里指PluginClass)派生,會引用一些不安全的感覺。實際上,這樣做也是沒意義的,插件應該“默默”地響應輸入設備的請求,然后提交結果給輸出設備。
在這種結構下,為了提供相同接口的多個不同實現,需要的工作變得異常復雜,如果插件可以用不同名字注冊自己(如Direct3DRenderer and OpenGLRenderer),但是引擎不知道哪個具體實現對用戶的選擇是有效的。假如把所有可能的實現列表硬編碼到程序中,那么使用插件結構的目的也沒有意義了。
假如插件系統通過一個框架或是庫(如游戲引擎) 實現,架構師也肯定會把功能暴露給應用程序使用。這樣,會帶來一些問題像如何在應用程序中使用插件,插件作者如何引擎的頭文件等,這包含了潛在的三者之間版本沖突的可能性。
單獨的工廠
接口,是被引擎清楚定義的,而不是插件。引擎通過定義接口來指導插件做什么工作,插件具體實現功能。我們讓插件注冊自己的引擎接口的特殊實現。當然直接創建插件實現類的實例并注冊是比較笨的做法。這樣使得同一時刻所有可能的實現同時存在,占用內存與CPU資源。解決的辦法是工廠類,它唯一的目的是在請求時創建另外類的實例。如果引擎定義了接口與插件通信,那么也應該為工廠類定義接口:
template<typename Interface>
class Factory {
  virtual Interface *create() = 0;
};
 
class Renderer {
  virtual void beginScene() = 0;
  virtual void endScene() = 0;
};
typedef Factory<Renderer> RendererFactory;

選擇1: 插件管理器
接下來應該考慮插件如何在引擎中注冊它們的工廠,引擎又如何實際地使用這些注冊的插件。一種選擇是與存在的代碼很好的接合,這通過寫插件管理器來完成。這使得我們可以控制哪些組件允許被擴展。
class PluginManager {
  void registerRenderer(std::auto_ptr<RendererFactory> RF);
  void registerSceneManager(std::auto_ptr<SceneManagerFactory> SMF);
};
當引擎需要一個渲染器時,它會訪問插件管理器,看哪些渲染器已經通過插件注冊了。然后要求插件管理器創建期望的渲染器,插件管理器于是使用工廠類來生成渲染器,插件管理器甚至不需要知道實現細節。
插件由動態庫組成,后者導出一個可以被插件管理器調用的函數,用以注冊自己:
void registerPlugin(PluginManager &PM);
插件管理器簡單地在特定目錄下加載所有dll文件,檢查它們是否有一個名為registerPlugin()的導出函數。當然也可用xml文檔來指定哪些插件要被加載。 

選擇 2: 完整地集成Fully Integrated
除了使用插件管理器,也可以從頭設計代碼框架以支持插件。最好的方法是把引擎分成幾個子系統,構建一個系統核心來管理這些子系統。可能像下面這樣:

class Kernel {
  StorageServer &getStorageServer() const;
  GraphicsServer &getGraphicsServer() const;
};
 
class StorageServer {
  //提供給插件使用,注冊新的讀檔器
  void addArchiveReader(std::auto_ptr<ArchiveReader> AL);
  // 查詢所有注冊的讀檔器,直到找到可以打開指定格式的讀檔器
  std::auto_ptr<Archive> openArchive(const std::string &sFilename);
};
 
class GraphicsServer {
  // 供插件使用,用來添加驅動
  void addGraphicsDriver(std::auto_ptr<GraphicsDriver> AF);
 
  // 獲取有效圖形驅動的數目
  size_t getDriverCount() const;
 //返回驅動
  GraphicsDriver &getDriver(size_t Index);
};
這里有兩個子系統,它們使用” Server”作為后綴。第一個Server內部維護一個有效圖像加載器的列表,每次當用戶希望加載一幅圖片時,圖像加載器被一一查詢,直到發現一個特定的實現可以處理特定格式的圖片。另一個子系統有一個GraphicsDrivers的列表,它們作為Renderers的工廠來使用。可以是Direct3DgraphicsDriver或是OpenGLGraphicsDrivers,它們分別負責Direct3Drenderer與OpenGLRenderer的創建。引擎提供有效的驅動列表供用戶選擇使用,通過安裝一個新的插件,新的驅動也可以被加入。

版本
在上面兩個可選擇的方法中,不強制要求你把特定的實現放到插件中。假如你的引擎提供一個讀檔器的默認實現,以支持自定義文件包格式。你可以把它放到引擎本身,當StorageServer 啟動時自動進行注冊。
現在還有一個問題沒有討論:假如你不小心的話,與引擎不匹配(例如,已經過時的)插件會被加載。子系統類的一些變化或是插件管理器的改變足以導致內存布局的改變,當不匹配的插件試圖注冊時可能發生沖突甚至崩潰。比較討厭的是,這些在調試時難與發現。 幸運的是,辨認過時或不正確的插件非常容易。最可靠的是方法是在你的核心系統中放置一個預處理常量。任何插件都有一個函數,它可以返回這個常量給引擎:
// Somewhere in your core system
#define MyEngineVersion 1;
 
// The plugin
extern int getExpectedEngineVersion() {
  return MyEngineVersion;
}
在這個常量被編譯到插件后,當引擎中的常量改變時,任何沒有進行重新編譯的插件它的 getExpectedEngineVersion ()方法會返回以前的那個值。引擎可以根據這個值,拒絕加載不匹配的插件。為了使插件可以重新工作,必須重新編譯它。當然,最大的危險是你忘記了更新常量值。無論如何,你應該有個自動版本管理工具幫助你。

 英文原文地址:http://www.nuclex.org/articles/building-a-better-plugin-architecture
 有示例代碼下載。

posted on 2007-04-17 15:54 清源游民 閱讀(5307) 評論(8)  編輯 收藏 引用 所屬分類: C++

FeedBack:
# re: 用C++實現的一種插件體系結構-----概述
2007-04-17 23:44 | 黃大仙
好  回復  更多評論
  
# re: 用C++實現的一種插件體系結構-----概述
2007-04-18 15:57 | Kooyu
很多時候經常變動的是接口而不是實現。  回復  更多評論
  
# re: 用C++實現的一種插件體系結構-----概述
2007-04-18 21:22 | missdeer
@Kooyu
接口經常變動就是設計得太糟糕了,要么就學COM,已有接口永遠不變,要么就添加新接口  回復  更多評論
  
# re: 用C++實現的一種插件體系結構-----概述
2007-04-18 21:48 | 清源游民
@missdeer
我也認為,如果接口經常變動的話,那么設計接口也沒什么意義了,設計接口就是希望能應對可能的變化。但真的是不存在一勞永逸的事情,這是個矛盾。  回復  更多評論
  
# re: 用C++實現的一種插件體系結構-----概述
2007-05-08 08:10 | Ulti
“如果把GPL組件封裝在插件中,你就不必發布插件的源碼”
這句應該譯成:
“如果把GPL組件封裝在插件中,你就只需要發布插件的源碼”  回復  更多評論
  
# re: 用C++實現的一種插件體系結構-----概述
2007-05-08 09:34 | 清源游民
致Ulti:
謝謝,是我粗心了。  回復  更多評論
  
# re: 用C++實現的一種插件體系結構-----概述
2008-08-07 15:18 | zhangws
如果把GPL組件封裝在插件中,你就不必發布插件的源碼。

樓主翻譯反了,
如果把GPL組件封裝在插件中,你就只需要發布插件的源碼。  回復  更多評論
  
# re: 用C++實現的一種插件體系結構-----概述
2008-08-07 15:20 | zhangws
樓主刪了吧,我沒有注意到有人提醒了,不好意思  回復  更多評論
  
<2007年3月>
25262728123
45678910
11121314151617
18192021222324
25262728293031
1234567

留言簿(35)

隨筆分類(78)

隨筆檔案(74)

文章檔案(5)

搜索

  •  

最新評論

閱讀排行榜

評論排行榜

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            欧美精品播放| 欧美在线视屏 | 亚洲三级影院| 亚洲精品乱码久久久久久日本蜜臀 | 欧美一区二区在线观看| 午夜日韩av| 久久精品中文字幕一区| 免播放器亚洲一区| 亚洲破处大片| 亚洲美女视频网| 亚洲自拍偷拍一区| 久久亚洲国产精品日日av夜夜| 久久资源av| 欧美日韩伦理在线| 欧美三级网页| 欧美精品一区三区| 国产精品久久久久三级| 国产欧美一区二区色老头| 国产在线拍揄自揄视频不卡99| 伊人色综合久久天天五月婷| 日韩一区二区电影网| 欧美中文字幕在线| 亚洲激情偷拍| 久久久www成人免费精品| 欧美日韩精品欧美日韩精品 | 1769国产精品| 亚洲无吗在线| 男人天堂欧美日韩| 亚洲免费视频网站| 欧美激情网站在线观看| 国产日韩精品一区二区| 日韩午夜免费视频| 蜜桃伊人久久| 亚洲欧美日韩精品| 欧美日韩日本视频| 亚洲精品在线观| 久久久久久久久久看片| 日韩亚洲一区二区| 美女视频黄 久久| 国产网站欧美日韩免费精品在线观看| 亚洲激情成人网| 久久一区二区三区av| 亚洲午夜羞羞片| 欧美日韩国产三区| 亚洲精品视频免费| 欧美激情一区二区三区高清视频 | 久久综合色88| 国产综合欧美| 久久精品国产一区二区三| 中日韩高清电影网| 欧美日韩高清在线播放| 亚洲娇小video精品| 久久五月天婷婷| 欧美亚洲视频一区二区| 国产日韩欧美二区| 欧美在线视频日韩| 午夜在线a亚洲v天堂网2018| 欧美性猛交99久久久久99按摩 | 99精品视频网| 欧美精品999| 99re热这里只有精品视频| 亚洲欧洲日产国产网站| 欧美黄色aaaa| 一区二区三区四区五区在线 | 亚洲精品一区二区三区在线观看| 亚洲一区999| 一本久久综合亚洲鲁鲁五月天| 欧美福利在线| 日韩一级裸体免费视频| 亚洲毛片一区| 国产精品久久久久久久浪潮网站| 亚洲一区bb| 欧美一级电影久久| 精品91久久久久| 亚洲国产精品t66y| 欧美日韩精品在线观看| 午夜精品福利一区二区蜜股av| 亚洲色在线视频| 国产亚洲人成a一在线v站 | 亚洲你懂的在线视频| 亚洲一级在线| 狠狠入ady亚洲精品| 欧美大片在线观看| 欧美激情一区二区在线| 亚洲一区二区三区四区视频| 亚洲欧美精品| 亚洲激情专区| 亚洲曰本av电影| 激情综合色丁香一区二区| 欧美激情在线免费观看| 欧美午夜在线一二页| 久久综合九色综合欧美就去吻 | 久久精品国产一区二区三区| 久久天天躁狠狠躁夜夜av| 99re亚洲国产精品| 性做久久久久久久免费看| 亚洲国产一区二区三区青草影视 | 亚洲一二三区在线观看| 亚洲国产精彩中文乱码av在线播放 | 这里只有视频精品| 久久精品人人做人人综合| 一区二区三区黄色| 久久精品中文| 亚洲欧美日韩系列| 欧美好骚综合网| 久久亚洲一区| 国产欧美在线视频| 日韩午夜免费| 亚洲精品黄色| 久久性色av| 久久精品夜色噜噜亚洲aⅴ| 欧美日韩成人在线| 欧美国产日韩一区二区| 国产视频在线观看一区二区三区| 亚洲精品国产精品乱码不99按摩| 国产亚洲一区二区三区在线播放| 亚洲九九爱视频| 亚洲精品国产精品国自产观看浪潮 | 久久综合影视| 国内精品视频666| 美女爽到呻吟久久久久| 亚洲激情综合| 亚洲欧美日韩人成在线播放| 国产一区二区日韩精品| 欧美大片网址| 亚洲伊人观看| 亚洲高清123| 欧美一区二区三区四区在线观看地址| 国产一级揄自揄精品视频| 欧美电影免费观看大全| 亚洲在线观看视频网站| 欧美国产精品日韩| 午夜精品久久久久99热蜜桃导演| 黄色精品免费| 国产精品多人| 欧美大片专区| 久久aⅴ国产欧美74aaa| 亚洲精品一区二| 美女任你摸久久| 午夜一区二区三区不卡视频| 亚洲国产91| 国产一区二区三区黄| 欧美日韩黄视频| 久久琪琪电影院| 亚洲女人天堂成人av在线| 亚洲高清在线视频| 久久青草欧美一区二区三区| 99精品视频免费全部在线| 黄色精品免费| 国产精品一区二区久久久久| 欧美另类在线观看| 久久一区免费| 久久精品观看| 亚洲欧美中文另类| 中文成人激情娱乐网| 亚洲二区视频| 欧美大胆成人| 老司机午夜精品视频| 午夜精品久久久久久久久久久久| 亚洲美女黄网| 亚洲精品三级| 亚洲欧洲一区二区在线观看| 黑人巨大精品欧美一区二区| 国产精品久久福利| 欧美日韩久久| 欧美日韩1区2区3区| 欧美成人蜜桃| 欧美成人精品三级在线观看| 久久久久免费观看| 久久久久9999亚洲精品| 销魂美女一区二区三区视频在线| 亚洲性人人天天夜夜摸| 日韩亚洲一区二区| 一级日韩一区在线观看| av成人免费| 国产精品99久久久久久久vr | 国产视频不卡| 国产亚洲日本欧美韩国| 国产亚洲一区在线播放| 国产在线国偷精品产拍免费yy| 国产亚洲精品一区二555| 国产日韩欧美在线看| 国产自产女人91一区在线观看| 国产偷久久久精品专区| 狠狠色狠狠色综合日日91app| 精品999在线播放| 亚洲激情社区| 国产日本欧美一区二区三区| 午夜天堂精品久久久久| 校园激情久久| 欧美一区二区在线| 欧美在线二区| 免费观看在线综合色| 欧美a级一区| 欧美日韩在线播| 国产欧美在线| 亚洲国产欧美一区二区三区丁香婷| 亚洲激情在线| 亚洲男人第一网站| 久久中文久久字幕|