C/C++:构建你自己的插件框架(四)

2014-11-24 12:36:29 · 作者: · 浏览: 2

在纯C++编程模型中你仅仅需要用C++开发你的插件。插件编程接口函数可以被实现为静态成员函数或者普通的静态/全局函数(毕竟C++主要是C的超集)。(这句不好翻啊:The object model can be your garden variety C++ object model.) 列表3包含示例游戏的C++对象模型。它基本上与列表2种的C对象模型相似。

#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++二重奏

在这个编程模型中你可以使用C或者C++来开发插件。当你注册你的对象时要指定编程语言。如果你创建一个平台并且你想提供给第三方开发者最终的自由是他们可以选择自己的编程语言及模型,混合并匹配C和C++插件时,这个模型将非常有用。

插件框架支持它,但实际的工作在于为你的应用设计一个既支持C又支持C++对象模型。每个对象类型需要同时实现C和C++的接口。这意味着你将有一个有着标准VTABLE布局的C++类以及一系列与虚拟方法相关的函数指针。这种结构非常复杂,我将不演示它。

要注意的是从插件开发人员的角度来说这种方法不会带来额外的复杂性。他们永远可以使用C或C++接口来开发C或C++的插件。

C/C++混合体

在该模型中,你必须在C对象模型的盖子之下用C++开发插件。这就包括了C++包裹类的创建,该包裹类实现了C++对象模型并包裹(wrap)相应C对象的。插件开发人员在这层上编程,将每个调用、参数及返回值在C和C++之间翻译。当实现你的应用程序对象模型时这需要额外的工作,但通常很直接。好处是对于插件开发这来说提供了一个有着完整C级兼容性的好的C++编程模型。我不会在示例游戏的上下文中演示它。

语言-链接矩阵

图1显示了各种不同的部署模型组合的利与弊(静态库vs. 动态库)以及编程语言的选择(C vs. C++)。


\

图1

为了本次讨论,如果使用C++插件,C/C++二重奏模型有C++限制和所需的先决条件,对于C插件,有C的限制和所需的先决条件。而且,C/C++混合模型不过是C模型,因为C++层被隐藏在插件实现后面。这些都让人迷惑,但底线是你有选项了,且插件框架允许你做出自己的决定,采用你自己认为合适的折中。它没有强迫你使用某个特定的模型,也没有瞄准最小公分母。

Copyleft (C) 2007, 2008 raof01. 本文可以用于除商业用途外的所有用途。若用于非商业用途,请保留此权利声明,并以超链接形式标明文章原始出版、作者信息和本声明;若要用于商业用途,请与作者联系,否则作者将使用法律来保证权利。

本文是关于开发跨平台C++插件系列的第二篇。第一篇详细描述了问题,探索了一些解决方案,并介绍了插件框架。本部分描述了架构以及构件在插件框架上,基于插件的系统的设计,插件的生命期,以及通用插件框架的内部。小心:代码遍布文章各个部分。

基于插件系统的架构 基于插件的系统可以分厂三个松散耦合的部分:有自己特有对象模型的主系统或应用;插件管理器;以及插件本身。插件遵从插件管理器的接口和协议,并实现对象模型接口。让我们用一个实际的例子来展示。主系统是一个基于回合的游戏。游戏发生在一个有着各种各样怪兽的战场上。英雄与怪兽搏斗知道他或者所有的怪兽死掉。列表以是英雄类的定义:

#ifndef HERO_H

#define HERO_H

#include

#include

#include

#include "object_model/object_model.h"

class Hero : public IActor

{

public:

Hero();

~Hero();

// IActor methods

virtual void getInitialInfo(ActorInfo * info);

virtual void play(ITurn * turnInfo);

private:

};

#endif

Listing one

BattleManager是驱动该游戏的引擎。它负责初始化hero和monster并且将他们安置在战场上。然后每个回合中,它通过调用每个actor(Hero或者Monster)的play()方法来攻击。

Hero和monster实现了IActor接口。Hero是一个内建的,有着预订义行为的游戏对象。另一方面,monster是由插件来实现的。这就允许游戏能够扩展为更多的新monster,并将新的monster的开发和游戏引擎的开发分离开来。PluginManager的工作就是抽象掉monster是由插件来产生并将他们展现给BattleManager 的事实,就像hero。这个方案允许一些内建的monster随同游戏一起发布,这些monster被静态链接进去,不是用插件实现的。BattleManager 甚至不应该知道有插件这样一回事。它应当在C++对象一级进行操作。这也使得其非常易于测试,因为你可以在测试代码中创建一些假的monster而不用写一个完整的插件。

PluginManager本身可以是通用的也可以是特定的。 通用的插件管理器不知道特定的底层对象模型。当一个C++的PluginManager实例化一个在插件中实现的新对象,它必须返回一个通用的接口,这样调用方必须将该实例转换成实际的接口。这看起来是有点丑,但很有必要。一个定制的插件管理器知道你的对象模型并且能够从底层的对象模型方面来操作。例如,一个为我们的游戏定制的PluginManager可以有返回IActor 接口的CreateMonster()方法。我所展示的PluginManager是通用的,但我将展示将对象模型特定的一层放到它上面会有多么简单。这是标准的实践,因为你不希望你的应用程序代码来处理显式类型转换。

插件系统生命期

现在该弄明白插件系统的生命期了