小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

[譯]C/C :構(gòu)建你自己的插件框架(1) - 程序設(shè)計(jì) - raof01-Haste ...

 xiaoqdu 2008-10-21
[譯]C/C++:構(gòu)建你自己的插件框架(1)
本文譯自Gigi SayfanDDJ上的專欄文章。Gigi Sayfan是北加州的一個(gè)程序員,email gigi@gmail.com.
本文是一系列討論架構(gòu)、開發(fā)和部署C/C++跨平臺(tái)插件框架的文章的第一篇。第一部分探索了一下現(xiàn)狀,調(diào)查了許多現(xiàn)有的插件/組件庫(kù),深入研究了二進(jìn)制兼容問題,并展現(xiàn)了一些該方案必要的一些屬性。
后續(xù)的文章用一個(gè)例子展示了可用于WindowLinux、Mac OS X并易于移植到其他系統(tǒng)的,具有工業(yè)級(jí)強(qiáng)度的插件框架。與其他類似框架相比,該框架有一些獨(dú)一無二的屬性,并且被設(shè)計(jì)為靈活、高效、易于編程、易于創(chuàng)建新插件,且同時(shí)支持CC++。同時(shí)還提供了多種部署選項(xiàng)(靜態(tài)或動(dòng)態(tài)庫(kù))。
我將開發(fā)一個(gè)簡(jiǎn)單的角色扮演游戲,可以自己增加非玩家角色的插件。游戲的引擎加載插件并無逢地集成他們。游戲展示了這些概念并且展示能夠?qū)嶋H運(yùn)行的代碼。

誰(shuí)需要插件?

插件是你想開發(fā)一個(gè)成功的動(dòng)態(tài)系統(tǒng)所需要的一種方式。基于插件的擴(kuò)展性是當(dāng)前擴(kuò)展&進(jìn)化一個(gè)系統(tǒng)的最具有實(shí)踐意義的安全方式。插件使得第三方開發(fā)人員可以為系統(tǒng)做增值工作,也可以使其他開發(fā)人員增加新的功能而不破壞現(xiàn)有的核心功能。插件能夠促進(jìn)將關(guān)注點(diǎn)分開,保證隱藏實(shí)現(xiàn)細(xì)節(jié),將測(cè)試獨(dú)立開來,并最具有實(shí)踐意義。

類似Eclipse的平臺(tái)實(shí)際上就是一個(gè)所有功能都由插件提供的骨架。Eclipse IDE自身(包括UIJava開發(fā)環(huán)境)僅僅是一系列掛在核心框架上的插件。

為什么選擇C++

眾所周知,當(dāng)用于插件時(shí),C++不是一個(gè)容易適應(yīng)新環(huán)境的東西。它非常依賴于編譯器和平臺(tái)。C++標(biāo)準(zhǔn)沒有指定任何應(yīng)用程序二進(jìn)制接口,這說明由不同的編譯器編譯出的庫(kù)甚至不同版本的庫(kù)是不兼容的。加上C++沒有動(dòng)態(tài)加載的概念,且每個(gè)平臺(tái)提供了自己的與其他平臺(tái)不兼容的解決方案,你就能夠了解。有少許重量級(jí)的解決方案試圖說明不僅僅是插件和對(duì)一些額外的運(yùn)行時(shí)的支持的依賴。但當(dāng)要求高性能系統(tǒng)時(shí), C/C++依然是僅有的實(shí)際可行的選項(xiàng)。

那里有什么?

在著手一個(gè)全新的框架之前,檢查現(xiàn)有的庫(kù)或者框架是值得的。我發(fā)現(xiàn)既有重量級(jí)的解決方案,如M$COMMozillaXPCOMCross-platform COM),或者只提供相當(dāng)基礎(chǔ)功能的如QT的插件以及少許關(guān)于創(chuàng)建C++插件的文章。一個(gè)有趣的庫(kù),DynObj,聲稱能解決二進(jìn)制兼容的問題(在相同的約束下)。也有一個(gè)由Daveed Vandervoorde提出,作為一個(gè)原生的概念給C++添加插件的提案。那是一個(gè)有趣的讀物,但感覺怪怪的。

沒有一個(gè)基礎(chǔ)的解決方案闡述了與創(chuàng)建工業(yè)級(jí)強(qiáng)度的基于插件的系統(tǒng)相關(guān)的大量的問題,如錯(cuò)誤處理,數(shù)據(jù)類型,版本控制,與框架代碼以及應(yīng)用代碼的分離。在進(jìn)入解決方案前,讓我們理解這個(gè)問題。

二進(jìn)制兼容問題

再次強(qiáng)調(diào),沒有標(biāo)準(zhǔn)的C++ ABI。不同的編譯器(甚至同一編譯器的不同版本)產(chǎn)生不同的目標(biāo)文件和庫(kù)。最明顯的表現(xiàn)是,不同編譯器實(shí)現(xiàn)不同的name mangling(譯注:這個(gè)術(shù)語(yǔ)我沒有找到合適的翻譯,意思是編譯時(shí)給函數(shù)名字加上一些標(biāo)識(shí),功能之一就是區(qū)分重載函數(shù))。這表明,通常情況下,你只能鏈接完全由同一個(gè)編譯器(版本號(hào)也要相同)編譯出來的目標(biāo)文件。甚至有很多編譯器沒有完全實(shí)現(xiàn)C++98標(biāo)準(zhǔn)中的功能。

對(duì)于這個(gè)問題有一些局部的解決方案。例如,如果你訪問一個(gè)C++對(duì)象時(shí)僅僅是通過虛擬指針(譯注:不知道說的是什么意思,很費(fèi)解,原文如下:if you access a C++ object only through a virtual pointer and call only its virtual methods you sidestep the name mangling issue)并只調(diào)用其虛函數(shù)九可以回避name mangling問題。然而,不能保證所有編譯器編譯出來的代碼運(yùn)行時(shí)在內(nèi)存中有相同的VTABLE布局,盡管它更穩(wěn)定(譯注:應(yīng)該指的是VTABLE在內(nèi)存中的布局各編譯器的實(shí)現(xiàn)更傾向于一致)。

如果你試圖動(dòng)態(tài)加載C++代碼將面對(duì)另一個(gè)問題——沒有直接的方法從Linux或者Mac OS X的動(dòng)態(tài)庫(kù)來加載并實(shí)例化C++的類(在Windows上,VC++支持)。

解決方案是使用一個(gè)具有C linkage的函數(shù)(因此編譯器不會(huì)對(duì)其進(jìn)行name mangling操作)作為一個(gè)工廠方法,來返回一個(gè)透明的handle給調(diào)用方。然后調(diào)用方將其轉(zhuǎn)換成正確的類(通常是一個(gè)純抽象基類)。當(dāng)然,這需要一些協(xié)作,而且僅當(dāng)應(yīng)用和庫(kù)所用編譯器的VTABLE內(nèi)存布局一致時(shí)才能工作。

解決兼容性的終極方法就是忘記C++,并使用純CAPI。在實(shí)際中,C對(duì)于所有的編譯器實(shí)現(xiàn)都是兼容的。后面我會(huì)戰(zhàn)士如何在C的兼容性基礎(chǔ)上達(dá)成C++編程模型。

基于插件的系統(tǒng)的體系結(jié)構(gòu)

一個(gè)基于插件的系統(tǒng)可以分成三個(gè)部分:

  • 領(lǐng)域相關(guān)系統(tǒng)(譯注:應(yīng)用程序的邏輯部分)
  • 插件管理器
  • 插件

領(lǐng)域相關(guān)系統(tǒng)通過插件管理器加載插件并創(chuàng)建其對(duì)象。一旦創(chuàng)建了插件對(duì)象且系統(tǒng)有某種指針或引用指向它,它就可以像其他對(duì)象一樣使用。我們將看到,這通常會(huì)需要一些特殊的析構(gòu)/清除工作。

插件管理器是相當(dāng)通用的一段代碼。它管理插件的生命期并且將他們暴露給主系統(tǒng)。它能找到并加載插件,初始化它們,注冊(cè)工廠函數(shù)并能夠卸載插件。它還應(yīng)當(dāng)能夠讓主系統(tǒng)遍歷已加載的插件或注冊(cè)的插件對(duì)象。

插件自身需要順應(yīng)插件管理器的協(xié)議并提供適應(yīng)主系統(tǒng)期望的對(duì)象。

實(shí)際上,你很少會(huì)看見如此清晰的分解(總之,在基于C++的插件系統(tǒng)上如此)。插件管理器經(jīng)常與領(lǐng)域相關(guān)系統(tǒng)緊密耦合。這是有很好的原因的。插件管理器需要提供某種類型的插件對(duì)象的最終實(shí)例。而且,插件的初始化經(jīng)常需要傳遞一些領(lǐng)域相關(guān)的信息和/或回調(diào)函數(shù)/服務(wù)。這可以由通用插件管理器輕松地做到。

插件部署模型

插件通常以動(dòng)態(tài)庫(kù)的形式部署。動(dòng)態(tài)庫(kù)允許插件的很多優(yōu)勢(shì)如熱交換(重新加載一個(gè)插件的新實(shí)現(xiàn)而無需關(guān)閉系統(tǒng)),而且需要更少的鏈接時(shí)間。然而,在某些情況下靜態(tài)庫(kù)是插件的最好選擇。例如,僅僅因?yàn)槟承┫到y(tǒng)不支持動(dòng)態(tài)庫(kù)(很多嵌入式系統(tǒng))。在其他的情況下,出于安全考慮,不允許加載陌生的代碼。有時(shí),核心系統(tǒng)會(huì)與一些預(yù)先加載好插件一起部署,而且靜態(tài)加載到主系統(tǒng)中使得它們更健壯(因此用戶不會(huì)無意中刪除它們)。

底線是,好的插件系統(tǒng)應(yīng)當(dāng)同時(shí)支持靜態(tài)和動(dòng)態(tài)插件。這可以讓你在不同的環(huán)境下,不同的約束下部署同一個(gè)基于插件的系統(tǒng)。

插件編程接口

所以關(guān)于插件的問題都是關(guān)于接口(譯注:要注意這里說的接口,不是C#JAVA的接口概念,理解為signature更合適)的?;诓寮南到y(tǒng)的基本觀念是:有某個(gè)中央系統(tǒng),通過定義良好的接口和協(xié)議,其在加載插件時(shí)不知道任何關(guān)于與插件通信的問題。

定義一系列函數(shù)作為插件導(dǎo)出的接口(動(dòng)態(tài)庫(kù)及靜態(tài)庫(kù))是幼稚的方法。這種方法在技術(shù)上是可行的,但在概念上是有瑕疵的(譯注:作者說話分量還是輕些)。原因是,插件應(yīng)當(dāng)支持兩種接口且只能有一套從插件導(dǎo)出的函數(shù)。這表明這兩種接口會(huì)被混合在一起。

第一層接口(及協(xié)議)是通用的插件接口。它使得中央系統(tǒng)可以初始化插件,并使插件可以在中央系統(tǒng)中注冊(cè)一系列的用于創(chuàng)建和銷毀對(duì)象以及全局的清理函數(shù)。通用插件接口不是與領(lǐng)域相關(guān)的,且可以被指定和實(shí)現(xiàn)為可服用的庫(kù)。第二層接口是由插件對(duì)象實(shí)現(xiàn)的功能性的接口。該接口是領(lǐng)域相關(guān)的,且世紀(jì)的插件必須非常謹(jǐn)慎的對(duì)其進(jìn)行設(shè)計(jì)和實(shí)現(xiàn)。中央系統(tǒng)應(yīng)當(dāng)知道該接口并能通過其與插件對(duì)象進(jìn)行交互。

列表1是一個(gè)指定了通用插件接口的頭文件。沒有深入細(xì)節(jié)并解釋所有事情之前,讓我們看看它提供了什么。

#ifndef PF_PLUGIN_H

#define PF_PLUGIN_H

 

#include <apr-1/apr_general.h>

 

#ifdef __cplusplus

extern "C" {

#endif

 

typedef enum PF_ProgrammingLanguage

{

  PF_ProgrammingLanguage_C,

  PF_ProgrammingLanguage_CPP

} PF_ProgrammingLanguage;

 

struct PF_PlatformServices_;

 

typedef struct PF_ObjectParams

{

  const apr_byte_t * objectType;

  const struct PF_PlatformServices_ * platformServices;

} PF_ObjectParams;

 

typedef struct PF_PluginAPI_Version

{

  apr_int32_t major;

  apr_int32_t minor;

} PF_PluginAPI_Version;

 

typedef void * (*PF_CreateFunc)(PF_ObjectParams *);

typedef apr_int32_t (*PF_DestroyFunc)(void *);

 

typedef struct PF_RegisterParams

{

  PF_PluginAPI_Version version;

  PF_CreateFunc createFunc;

  PF_DestroyFunc destroyFunc;

  PF_ProgrammingLanguage programmingLanguage;

} PF_RegisterParams;

 

typedef apr_int32_t (*PF_RegisterFunc)(const apr_byte_t * nodeType, const PF_RegisterParams * params);

typedef apr_int32_t (*PF_InvokeServiceFunc)(const apr_byte_t * serviceName, void * serviceParams);

 

typedef struct PF_PlatformServices

{

  PF_PluginAPI_Version version;

  PF_RegisterFunc registerObject;

  PF_InvokeServiceFunc invokeService;

} PF_PlatformServices;

 

typedef apr_int32_t (*PF_ExitFunc)();

 

typedef PF_ExitFunc (*PF_InitFunc)(const PF_PlatformServices *);

 

#ifndef PLUGIN_API

  #ifdef WIN32

    #define PLUGIN_API __declspec(dllimport)

  #else

    #define PLUGIN_API

  #endif

#endif

 

extern

#ifdef  __cplusplus

"C"

#endif

PLUGIN_API PF_ExitFunc PF_initPlugin(const PF_PlatformServices * params);

 

#ifdef  __cplusplus

}

#endif

#endif /* PF_PLUGIN_H */

列表1

首先你應(yīng)當(dāng)注意到這是一個(gè)C文件。這允許插件框架可以由純C系統(tǒng)編譯使用并可用來寫純C插件。但是,它不僅僅局限在C上,且實(shí)際上大多數(shù)情況下用在C++中。

枚舉類型PF_ProgrammingLanguage允許插件聲明到用C++實(shí)現(xiàn)的插件管理器中。

PF_ObjectParams是一個(gè)抽象的結(jié)構(gòu)體,創(chuàng)建插件時(shí)用于傳遞參數(shù)給插件對(duì)象。

PF_PluginAPI_Version被用于商討版本問題,并保證插件管理器只加載合適版本的插件。

函數(shù)指針PF_CreateFuncPF_DestroyFunc(由插件來實(shí)現(xiàn))允許插件管理器來創(chuàng)建和銷毀插件對(duì)象(每個(gè)插件注冊(cè)這樣的函數(shù)到插件管理器中。)

PF_RegisterParams結(jié)構(gòu)體包含初始化時(shí)插件必須提供給插件管理器的所有信息。(版本信息,創(chuàng)建/銷毀函數(shù),編程語(yǔ)言)

PF_RegisterFunc(由插件管理器實(shí)現(xiàn))允許每個(gè)插件為每種它所支持的對(duì)象類型注冊(cè)一個(gè)PF_RegisterParams結(jié)構(gòu)體。注意這個(gè)方案允許一個(gè)插件注冊(cè)一個(gè)對(duì)象的不同版本和多個(gè)對(duì)象類型。

PF_InvokeService函數(shù)指針是一個(gè)通用的函數(shù),查檢可以用其來調(diào)用主系統(tǒng)提供的服務(wù)如日志、事件通知及錯(cuò)誤報(bào)告。其簽名(signature)包括服務(wù)名稱和指向一個(gè)參數(shù)結(jié)構(gòu)體不透明的指針。插件應(yīng)當(dāng)知道可用的服務(wù)以及如何調(diào)用它們(或者,如果你希望使用PF_InvokeService,你可以自己實(shí)現(xiàn)服務(wù)。)

PF_PlatformServices結(jié)構(gòu)體聚集了我剛剛提到所有的由平臺(tái)提供給插件的服務(wù)(版本,注冊(cè)對(duì)象,執(zhí)行服務(wù)函數(shù))。該結(jié)構(gòu)體在初始化時(shí)傳遞給每個(gè)插件。

PF_ExitFunc定義了插件退出函數(shù),由插件來實(shí)現(xiàn)。

PF_InitFunc定義了插件初始化函數(shù)指針。

PF_initPlugin是動(dòng)態(tài)插件(由動(dòng)態(tài)鏈接庫(kù)/共享庫(kù)來部署的插件)實(shí)際的初始化函數(shù)的簽名(signature)。從動(dòng)態(tài)插件中導(dǎo)出它的名字,因此插件管理器可以在加載插件時(shí)調(diào)用它。它接受一個(gè)指向PF_PlatformServices結(jié)構(gòu)體的指針,因此所有的服務(wù)在初始化時(shí)立刻可用(這是注冊(cè)對(duì)象的正確時(shí)機(jī))并返回一個(gè)指向退出函數(shù)的指針。

注意靜態(tài)插件(實(shí)現(xiàn)在靜態(tài)庫(kù)中且直接連接到主執(zhí)行體中)應(yīng)當(dāng)實(shí)現(xiàn)一個(gè)有C linkageinit函數(shù),但禁止將其命名為PF_initPlugin。原因是如果有多個(gè)靜態(tài)插件,他們都將有一個(gè)同樣的初始化函數(shù)名字,你的編譯器痛恨這個(gè)。

靜態(tài)插件的初始化有所不同。他們必須顯式地由主執(zhí)行體初始化。主執(zhí)行體將通過PF_InitFunc的簽名調(diào)用它們的初始化函數(shù)。很不幸,這意味著每當(dāng)一個(gè)新的靜態(tài)插件加入/移出系統(tǒng)時(shí),主執(zhí)行體需要被修改,并且各種各樣的init函數(shù)的名字必須是對(duì)等的(coordinated)。

有一種試圖解決該問題的技術(shù)叫做“自動(dòng)注冊(cè)”。自動(dòng)注冊(cè)通過靜態(tài)庫(kù)中一個(gè)全局的對(duì)象來達(dá)到目的。該對(duì)象在main()事件啟動(dòng)之前被構(gòu)建。該全局對(duì)象可以請(qǐng)求插件管理器來初始化靜態(tài)插件(通過傳遞插件的init()函數(shù)指針)。不幸的是,這種方案在VC++中不能工作。

撰寫插件

撰寫插件意味著什么?插件框架是非常generic,并且不提供任何可以與你的應(yīng)用交互的切實(shí)的對(duì)象。你必須在插件框架上構(gòu)建你自己的應(yīng)用程序模型。這意味著你的應(yīng)用程序(加載插件)以及插件自身必須同意并通過某種模型來協(xié)作。通常這表明應(yīng)用程序期待插件提供暴露某種特定API的某種類型的對(duì)象。插件框架將提供注冊(cè)、枚舉及加載這些對(duì)象的基礎(chǔ)設(shè)施。示例1是一個(gè)叫做IActorC++接口的定義。它有兩個(gè)操作——getInitialInfo()play()。注意該接口不是充分的,因?yàn)?/span>getInitialInfo()期望一個(gè)指向名為ActorInfo的結(jié)構(gòu)體的指針,且play()期望一個(gè)指向另一個(gè)叫做ITurn接口的指針。這是實(shí)際的一個(gè)案例,你必須設(shè)計(jì)并指定整個(gè)對(duì)象模型。

struct IActor

{

  virtual ~IActor() {}

  virtual void getInitialInfo(ActorInfo * info) = 0;

  virtual void play( ITurn * turnInfo) = 0;

};

示例1

每個(gè)插件可以注冊(cè)多個(gè)實(shí)現(xiàn)了IActor接口的類型。當(dāng)應(yīng)用程序決定示例化一個(gè)由插件注冊(cè)的對(duì)象,它將調(diào)用注冊(cè)的,由插件實(shí)現(xiàn)的PF_CreateFunc函數(shù)。插件負(fù)責(zé)創(chuàng)建一個(gè)合適的對(duì)象并將其返回給應(yīng)用程序。返回類型指定為void *是因?yàn)閷?duì)象創(chuàng)建操作是通用插件框架的一部分,該部分不知道任何關(guān)于特定IActor接口的信息。應(yīng)用程序隨后將void *轉(zhuǎn)換到IActor *,然后就可以在整個(gè)接口中使用它,好像它是一個(gè)正常的對(duì)象。當(dāng)應(yīng)用程序使用完IActor對(duì)象后,它執(zhí)行注冊(cè)的由插件實(shí)現(xiàn)的PF_DestroyFunc函數(shù),然后插件銷毀actor對(duì)象。目前不好考慮虛擬的析構(gòu)函數(shù),我會(huì)在后面的部分討論它。

編程語(yǔ)言支持

在二進(jìn)制兼容性部分我解釋了你可以利用C++vtable一級(jí)的兼容性,如果你的編譯器滿足的話。你也可以使用C一級(jí)的兼容性,這樣你就可以使用不同的編譯器來構(gòu)建應(yīng)用程序和插件,但你將被局限在C的交互上。你的應(yīng)用程序?qū)ο竽P捅仨毷腔?/span>C的。你不能使用好的C++接口如IACTOR,但你必須設(shè)計(jì)一個(gè)相似的C接口。

C

在純C的編程模型中你只需要用C開發(fā)插件。當(dāng)你實(shí)現(xiàn)PF_CreateFunc函數(shù)時(shí)你返回一個(gè)在你的應(yīng)用程序C對(duì)象模型中與其它C對(duì)象交互的C對(duì)象。所有的話題都是關(guān)于C對(duì)象和C對(duì)象模型的。所有人都知道C是一個(gè)過程語(yǔ)言,沒有對(duì)象的概念。然而C提供了足夠的抽象機(jī)制來實(shí)現(xiàn)對(duì)象以及多態(tài)(在此處是必須的)并支持面向?qū)ο蟮木幊谭盒?。?shí)際上,最初的C++編譯器是一個(gè)C編譯器的事實(shí)上的一個(gè)前端(front-end)。它根據(jù)C++代碼產(chǎn)生C代碼,然后使用一個(gè)普通的C編譯器來編譯該C代碼。它的名字Cfront說明了一切。

使用包含函數(shù)指針的結(jié)構(gòu)體(譯注:就可以獲得OO特性)。每個(gè)函數(shù)的簽名應(yīng)當(dāng)接受它所屬結(jié)構(gòu)體作為第一個(gè)參數(shù)該結(jié)構(gòu)體也可以包含其它的數(shù)據(jù)成員。這樣提供了(與C++類有關(guān)的簡(jiǎn)單的土語(yǔ))如:封裝(狀態(tài)和行為捆綁)、繼承(通過將基結(jié)構(gòu)體的對(duì)象作為第一個(gè)數(shù)據(jù)成員)以及多態(tài)(通過設(shè)置不同的函數(shù)指針。)(譯注:沒錯(cuò),這就是用C來編寫OO程序的基本要求和方法,我也用C寫過OO程序)。

C不支持析構(gòu)函數(shù)、函數(shù)及操作符重載,名字空間,因此你定義接口時(shí)只有很少的選項(xiàng)。這也許是“塞翁失馬,焉知非福”,因?yàn)榻涌趹?yīng)該被可能掌握C++另一個(gè)子集的其它人所使用。減少語(yǔ)言的范圍可能會(huì)提升你的接口的簡(jiǎn)單性和可用性。

我將在插件框架的后續(xù)文章中探究OOC。列表2包含了陪伴該文章系列(僅僅是投你所好)的示例游戲的C對(duì)象模型。如果你快速瀏覽一下你會(huì)看見它甚至支持集合以及遍歷。

#ifndef C_OBJECT_MODEL

#define C_OBJECT_MODEL

 

#include <apr-1/apr.h>

 

#define MAX_STR 64 /* max string length of string fields */

 

typedef struct C_ActorInfo_

{

  apr_uint32_t id;

  apr_byte_t   name[MAX_STR];

  apr_uint32_t location_x;

  apr_uint32_t location_y;

  apr_uint32_t health;

  apr_uint32_t attack;

  apr_uint32_t defense;

  apr_uint32_t damage;

  apr_uint32_t movement;

} C_ActorInfo;

 

typedef struct C_ActorInfoIteratorHandle_ { char c; } * C_ActorInfoIteratorHandle;

typedef struct C_ActorInfoIterator_

{

  void (*reset)(C_ActorInfoIteratorHandle handle);

  C_ActorInfo * (*next)(C_ActorInfoIteratorHandle handle);

 

  C_ActorInfoIteratorHandle handle;

} C_ActorInfoIterator;

 

typedef struct C_TurnHandle_ { char c; } * C_TurnHandle;

typedef struct C_Turn_

{

  C_ActorInfo * (*getSelfInfo)(C_TurnHandle handle);

  C_ActorInfoIterator * (*getFriends)(C_TurnHandle handle);

  C_ActorInfoIterator * (*getFoes)(C_TurnHandle handle);

 

  void (*move)(C_TurnHandle handle, apr_uint32_t x, apr_uint32_t y);

  void (*attack)(C_TurnHandle handle, apr_uint32_t id);

 

  C_TurnHandle handle;

} C_Turn;

 

typedef struct C_ActorHandle_ { char c; } * C_ActorHandle;

typedef struct C_Actor_

{

  void (*getInitialInfo)(C_ActorHandle handle, C_ActorInfo * info);

  void (*play)(C_ActorHandle handle, C_Turn * turn);

 

  C_ActorHandle handle;

} C_Actor;

 

#endif

列表2

C++

在純C++編程模型中你僅僅需要用C++開發(fā)你的插件。插件編程接口函數(shù)可以被實(shí)現(xiàn)為靜態(tài)成員函數(shù)或者普通的靜態(tài)/全局函數(shù)(畢竟C++主要是C的超集)。(這句不好翻?。?/span>The object model can be your garden variety C++ object model. 列表3包含示例游戲的C++對(duì)象模型。它基本上與列表2種的C對(duì)象模型相似。

#ifndef OBJECT_MODEL

#define OBJECT_MODEL

 

#include "c_object_model.h"

 

typedef C_ActorInfo ActorInfo;

 

struct IActorInfoIterator

{

  virtual void reset() = 0;

  virtual ActorInfo * next() = 0;

};

 

struct ITurn

{

  virtual ActorInfo * getSelfInfo() = 0;

  virtual IActorInfoIterator * getFriends() = 0;

  virtual IActorInfoIterator * getFoes() = 0;

 

  virtual void move(apr_uint32_t x, apr_uint32_t y) = 0;

  virtual void attack(apr_uint32_t id) = 0;

};

 

struct IActor

{

  virtual ~IActor() {}

  virtual void getInitialInfo(ActorInfo * info) = 0;

  virtual void play( ITurn * turnInfo) = 0;

};

 

#endif

列表3

C/C++二重奏

在這個(gè)編程模型中你可以使用C或者C++來開發(fā)插件。當(dāng)你注冊(cè)你的對(duì)象時(shí)要指定編程語(yǔ)言。如果你創(chuàng)建一個(gè)平臺(tái)并且你想提供給第三方開發(fā)者最終的自由是他們可以選擇自己的編程語(yǔ)言及模型,混合并匹配CC++插件時(shí),這個(gè)模型將非常有用。

插件框架支持它,但實(shí)際的工作在于為你的應(yīng)用設(shè)計(jì)一個(gè)既支持C又支持C++對(duì)象模型。每個(gè)對(duì)象類型需要同時(shí)實(shí)現(xiàn)CC++的接口。這意味著你將有一個(gè)有著標(biāo)準(zhǔn)VTABLE布局的C++類以及一系列與虛擬方法相關(guān)的函數(shù)指針。這種結(jié)構(gòu)非常復(fù)雜,我將不演示它。

要注意的是從插件開發(fā)人員的角度來說這種方法不會(huì)帶來額外的復(fù)雜性。他們永遠(yuǎn)可以使用CC++接口來開發(fā)CC++的插件。

C/C++混合體

在該模型中,你必須在C對(duì)象模型的蓋子之下用C++開發(fā)插件。這就包括了C++包裹類的創(chuàng)建,該包裹類實(shí)現(xiàn)了C++對(duì)象模型并包裹(wrap)相應(yīng)C對(duì)象的。插件開發(fā)人員在這層上編程,將每個(gè)調(diào)用、參數(shù)及返回值在CC++之間翻譯。當(dāng)實(shí)現(xiàn)你的應(yīng)用程序?qū)ο竽P蜁r(shí)這需要額外的工作,但通常很直接。好處是對(duì)于插件開發(fā)這來說提供了一個(gè)有著完整C級(jí)兼容性的好的C++編程模型。我不會(huì)在示例游戲的上下文中演示它。

語(yǔ)言-鏈接矩陣

1顯示了各種不同的部署模型組合的利與弊(靜態(tài)庫(kù) vs. 動(dòng)態(tài)庫(kù))以及編程語(yǔ)言的選擇(C vs. C++)。

1

為了本次討論,如果使用C++插件,C/C++二重奏模型有C++限制和所需的先決條件,對(duì)于C插件,有C的限制和所需的先決條件。而且,C/C++混合模型不過是C模型,因?yàn)?/span>C++層被隱藏在插件實(shí)現(xiàn)后面。這些都讓人迷惑,但底線是你有選項(xiàng)了,且插件框架允許你做出自己的決定,采用你自己認(rèn)為合適的折中。它沒有強(qiáng)迫你使用某個(gè)特定的模型,也沒有瞄準(zhǔn)最小公分母。

Copyleft (C) 2007, 2008 raof01. 本文可以用于除商業(yè)用途外的所有用途。若用于非商業(yè)用途,請(qǐng)保留此權(quán)利聲明,并以超鏈接形式標(biāo)明文章原始出版、作者信息和本聲明;若要用于商業(yè)用途,請(qǐng)與作者聯(lián)系,否則作者將使用法律來保證權(quán)利。

 

 原文地址 http://www./cpp/204202899?cid=RSSfeed_DDJ_Cpp
 TAG C/C++ 插件 框架

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多