如何写一个c++插件化系统(一)

2014-11-24 07:55:12 · 作者: · 浏览: 3
1.为什么需要插件化系统
  “编程就是构建一个一个自己的小积木, 然后用自己的小积木搭建大系统”。
  但是程序还是会比积木要复杂, 我们的系统必须要保证小积木能搭建出大的系统(必须能被组合),有必须能使各个积木之间的耦合降低到最小。
  传统的程序结构中也是有模块的划分,但是主要有如下几个缺点:
    a: c++二进制兼容
    b: 模块对外暴露的东西过多,使调用者要关心的东西过多
    c: 封装的模块只是作为功能的实现者封装,而不是接口的提供者
    d: 可替换性和可扩展性差
  而插件式的系统架构就是为了解决这样的问题。插件化设计的优点?插件化设计就是为了解决这些问题的,所以以上的缺点就是咱的优点
2.插件话系统的原理
  指导性原则:“面向接口编程而不是实现编程”
  其接口的定义为interface, 其实转换一下的意思是面向纯虚类编程,当然也可以包装成面向服务和组件编程。
  如我可以这样定义一个接口(interface)
1
2
3
4
5
interfacecptf IRole{
  virtual cptf ::ulong getHealth() = 0;
  virtual cptf ::ulong getHurt() = 0;
  virtual wstring getName() = 0;
};
  插件的目标就是实现IRole, 业务层的目标就是调用IRole, 业务层不知道IRole具体是如何实现的,而实现者也不用关心业务层是如何调用的。
3.插件化系统的目标
  1). 使用者能通过规范,开发自己的插件,实用已有的插件,插件又能控制对外暴露的内容。
  2). 运行时候能动态安装、启动、停在、卸载
  3). 每一个插件提供一个或多个服务,其他插件是根据接口来获取服务提供者
4. 一个插件化系统应该是怎么构成的
  OSGI,Java中影响力最大的插件化系统就是OSGI标准
  OSGI的定义:The dynamic module system for java
  借鉴osgi对插件系统的定义,我认为一个典型的插件系统应该有如下几个方面构成:
  “基础库+微内核+系统插件+应用插件”
  其中微内核 负责如下功能:
    1、 负责插件的加载,检测,初始化。
    2、 负责服务的注册。
    3、 负责服务的调用。
    4、 服务的管理。
5. 一个简单场景的随想
  比如设计下如下的游戏场景:一个RPG游戏, 玩家控制一个英雄,在场景中有不同的怪物,而且随着游戏的更新,
  英雄等级的提升又会有不同的怪物出现, 这里就想把怪物设计为插件。
  首先工程是这样的布局的
首先要在做的是定义接口, 这里我需要一个英雄的接口,有需要一个怪物的接口。
interfacecptf IHero : public cptf ::core:: IDispatch
, public IRole {
virtual cptf ::ulong attack() = 0;
};
interfacecptf IOgre : public cptf ::core:: IDispatch
, public IRole {
};
然后作为插件我需要实现一个Hero, 和多个Ogre
class Hero : public ServiceCoClass
, public ObjectRoot
, public cptf ::core:: IDispatchImpl{
class Wolf : public ServiceCoClass
, public ObjectRoot
, public cptf::core ::IDispatchImpl< IOgre>
class Tiger : public ServiceCoClass
, public ObjectRoot
, public cptf::core ::IDispatchImpl< IOgre> 
最后,在主工程用我要用到这些插件
复制代码
void BattleMannager ::run()
{
hero_ = static_cast(serviceContainer_. getService(Hero_CSID , IHero_IID));
if (!hero_ )return;
printHero(hero_ );
list services = serviceContainer_ .getServices( IOgre_IID);
list ogres = CastUtils::parentsToChildren (services );
for_each(ogres .begin(), ogres.end (), bind(&BattleMannager ::printOgre, _1));
services = serviceContainer_ .getServices( IHumanOgre_IID);
list hummanOgres = CastUtils::parentsToChildren (services );
for_each(hummanOgres .begin(), hummanOgres.end (), bind(&BattleMannager ::printHumanOgre, _1));
}
复制代码
  以上, 因为逻辑层和插件实现层都已经好了, 整个流程也已经跑通,但是还是的疑问:服务是怎么加载的?
6. 如何进行插件的加载以及服务的注册
借鉴OSGI, 我这里把系统设计为bundle+service的组合。 bundle是service的容器,service是功能的具体实现者。
  在windows下,bundle用dll来表示。
那bundle在windwos下加载就很简单了LoadLibrary Api就行了
但是再c++中dll的接口还必须要考虑的一个问题就是c++的二进制兼容性:现在没有标准的 C++ ABI。这意味着,不同编译器(甚至同一编译器的不同版本)会编译出不同的目标文件和库。这个问题导致的最显而易见的问题就是,不同编译器会使用不同的名称改写算法。这样对插件的接口来说是致命的。当然我们可以用c api来作为接口,但是这样势必会对整体的设计产生影响,而且作为一个装B的c++程序员,我们怎么能容忍要借用低级语言的特性来实现我们的功能呢。当然幸亏还有另外一种方式,那就是虚表。当然不是所有的c++编译器对虚表的实现也是不一样的(好吧~~),但是至少主流(多主流~~不能确定)的编译器虚表都是在对象的第一个位置。好吧,现在决定用虚表来对插件接口的实现了,所