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

2014-11-24 07:55:12 · 作者: · 浏览: 1
以我们就可以用这样的方式来计算具体实现类的地址了
#define CPTF_PACKING 8
#define cptf_offsetofclass (base, derived) \
(( cptf::ulong )(static_cast< base*>((derived *)CPTF_PACKING))- CPTF_PACKING)
  哇,好神奇的代码, 这个是为什么呢。 这个就需要对c++内存对象模型需要深入得了解了,可能需要拜读,这里篇幅有限这里就不解释了。但是如果有看官想要问“你为什么这么天才能想出这样的写法?”,虽然我很想说我很天才,但是其实正是情况是我参考的atl中的 源码,而且整个插件加载过程我都是山寨了atl中的相关代码的。
但是还是有一个问题, 在GameMain中,认识的是IHero, 根本不知道有个Hero的实现,所有可能有这样的代码IHero* hero = New Hero() 这样动作。
那我们要如何进行这样的new动作。 当然我们说Hero是在Role dll中的, 在dll被加载的时候可以new Hero, 然后把hero对象的地址放到某个堆中,标志让GameMain使用。作为一个转换的伪设计人员, 我也是认为这样会有性能问题的, 我不仅要做到加载, 还要做到懒加载。
那如何做到懒加载呢?
感谢微软,在 vc++中有机制帮我们做到,在其他的编译器中也会有其他的实现,但是这里我们只做了vc++中的实现。
首先声明一个自己的段,段名可以叫cptf:
#pragma section ("CPTF$__a", read, shared )
#pragma section ("CPTF$__z", read, shared )
#pragma section ("CPTF$__m", read, shared )
  然后在编译的时候,把具体实现的类的Create函数地址放到这个段中
#define CPTF_OBJECT_ENTRY_AUTO (class) \
  __declspec(selectany ) AutoObjectEntry __objMap_##class = {class::clsid (), class:: creatorClass_::createInstance }; \
  extern "C" __declspec( allocate("CPTF$__m" )) __declspec(selectany ) AutoObjectEntry* const __pobjMap_ ##class = &__objMap_ ##class; \
  CPTF_OBJECT_ENTRY_PRAGMA(class )
 最后在加载的时候,变量这个段,如果csid命中,则调用Create方法
复制代码
inline bool cptfModuleGetClassObject( const CptfServiceEntities * cpfgModel
, const cptf::IID & csid
, const cptf::IID & iid
, void** rtnObj)
{
bool rtn (false);
assert(cpfgModel );
for (AutoObjectEntry ** entity = cpfgModel->autoObjMapFirst_
; entity != cpfgModel ->autoObjMapLast_; ++entity)
{
AutoObjectEntry* obj = *entity;
if (obj == NULL) continue;
if (obj ->crateFunc != NULL && csid == obj-> iid){
rtn = obj ->crateFunc( iid, rtnObj );
break;
}
}
return rtn ;
}
复制代码
  总结下流程:
    1. GameMian使用的是IHero,
    2. Hero是IHero的实现者,在编译的规程中,把Create Hero的方法编译到固定段中
    3. GameMian进行new的时候其实调用的是Dll固定段中的函数地址
    4. 利用 上面的cptf_offsetofclass 宏实现对IHero的
7. 服务的管理
  每一个服务都需要一个id来标志它, 这里就用guid, 命名为IID---interface id
  每一个服务的实现者也必须要有id来标志, 这也是一个guid, 命名为csid
  我们把服务和服务实现者的管理信息用配置文件管理起来,services.xml, 对Hero的定义
复制代码
Role.dll
500851c0-7c2a-11e3-8c28-bc305bacf447
hero
Hero
99f9dd8f-7c1a-11e3-9f9d-bc305bacf447
IHero
复制代码
  当然一个插件的管理器也是必须的, 管理Service的注册,缓存,析构、获取,查询等。这里用ServiceContainer实现
8. 基于插件的架构
基于插件 系统的架构:
主要分三部分: 1. 使用其对象模型的主系统或主应用程序
2. 插件管理器
3. 插件
所有的插件但是从IService, 是参考Com中IUnkown
interfacecptf IService{
virtual cptf ::ulong addRef() = 0;
virtual cptf ::ulong release() = 0;
virtual bool queryInterface( const cptf ::IID& iid, void**rntObj ) = 0;
};
  其实插件的内核并不复杂,复杂的是对插件接口的定义和封装,如何根据不同的业务场景抽象出不同的interface。
9. 源代码
  本文不是很水的理论,所有的理论都是经过代码验证的。
本文涉及到的代码在我的github上,https://github.com/sld666666/cptf
工程的目标是建立一个跨平台的c++插件开发框架, 现在的是一个能成功在vc++下运行demo的插件化framework
用了boost和stl,如果要深入了解core中的代码,还需要对模板有了解, 水深请勿轻易尝试
当然有的看官会对core中的