标准C++实现反射特性

来源:互联网 发布:仓库软件有哪些 编辑:程序博客网 时间:2024/04/28 12:25

文章贴不全,从这下,有代码和本文

http://download.csdn.net/source/2135630

需求

         程序员通常有一个说法,叫做数据结构+算法=程序。随着软件项目的日渐复杂,这个公式我认为应该修订成数据结构+算法+构架=程序,而在这个google baidu横行,从蛋糕制作到原子弹设计原理都随时可查的年代,数据结构和算法对于一个成熟的程序员,已经不是主要的难点。于是,好的系统构架和采用的设计模式,成为一个软件的设计流程是否能顺利高效实现的关键。

         最近几年,我开始学习和使用.net框架开发游戏项目,.net框架的设计,个人认为真的非常优秀。在.net众多优秀的特性中,我觉得反射,对于系统设计影响极为重大,一个系统是否支持反射(Reflection),甚至会决定系统体系构架。比如在Reflection体系下,实现远程调用(RPC)就会很顺畅,而.netGUI变得无比痛快的PropertyGrid.SelectObject也是只有在Reflection上才会如此方便强大的实现。

         Reflection很诱人,以致于让我一直想在C++上实现这个特性,在C++0x规范难产的情况下,更为激进的Reflection特性很难近几年得到标准C++的支持,于是,我在终于08年初无聊的时候做了一个简单的反射实现。随后,由于没有实际应用,一下就搁置了1年多,最近,我又翻出来这段代码,折腾了一下,相比那时版本,现在的Reflection功能更为强大,容错性更强,而使用起来更加方便。这里在这里除了把最新代码发布出来,还吧一些设计经验教训拿出来和大家分享,也算是应了最开始在blog上说要写相关文章的承诺。

         作为一个Reflection体系,参考.net框架,我们需要起码能做如下几件事:

1.  对象动态创建(包括非缺省构造函数调用)

2.  运行时类型信息(获取类,成员变量,函数信息,继承关系等)

3.  类成员函数的Invoke(动态调用成员函数)

基本假设

         就算.net系统,也有一些约定是不能违背的,我的Reflection系统也是要建立在一定基本假设上的。

1.  单继承,虽然个人感觉理论上MI支持Reflection也不是不可能,但是java.net等支持Reflection的体系下,都不支持MI,而只能单继承,我想我还是别去想着怎么搞他了,于是我假定了我的Reflection系统只支持SI,起码ClassType的结构,我就要好写不少。

2.  C++编译器起码要支持偏特化,VC6一类的就别来了,不支持偏特化的编译器,作一个选择子都能让人郁闷无比。而TypeList一类的特性,需要编译器在编译时做递归处理,那些古董编译器还是休息下吧。

3.  尽可能的不要改动平时C++类的代码设计和书写习惯。

设计概要

         让我们推导一下,为了实现Reflection的那些特性,我们大体需要做一些什么 。首先,我们需要一些数据结构,来描述Class,Member,Methord。然后我们需要有一个流程,能够自动获取我们感兴趣的那些数据,最后,我们需要运行时期处理这些数据。

         下面给出部分数据结构的定义

         struct VClassType

     {

         struct VMember

         {

              VMember( VClassType* t , LPCTSTR n , UINT o , BOOL bC , BOOL bP )

                   : Type(t)

                   , Name(n)

                   , Offset(o)

                   , IsConst(bC)

                   , IsPointer(bP)

              {

 

              }

              ~VMember()

              {

                   for( size_t i=0 ; i<Attrs.size() ; i++ )

                   {

                       delete Attrs[i];

                   }

                   Attrs.clear();

              }

              VClassType*        Type;

              VString            Name;

              UINT          Offset;

 

              BOOL          IsConst;

              BOOL          IsPointer;

              std::vector<VAttribute*>    Attrs;

         };

 

         VClassType()

              : Type(OT_ReflectObject)

              , Super(NULL)

              , ClassID(0)

              , CreateObject(NULL)

         {

         }

         VClassType( ObjType t , LPCTSTR fn , vIID id ,VClassType* s=NULL )

              : Type(t)

              , FullName(fn)

              , ClassID(id)

              , Super(s)

              , CreateObject(NULL)

         {

         }

         VFX_API ~VClassType();

         VClassType*            Super;

 

         ObjType                Type;

         VString                FullName;

         vIID               ClassID;

 

         std::vector<VMember>             Members;

         std::vector<VMethodBase*>        Methods;

 

         typedef void*(*fnCreateObject)();

 

         fnCreateObject         CreateObject;

 

         template< typename type >

         type* vfxCreateObject(){

              if( CreateObject )

                   return (type*)CreateObject();

              return NULL;

         }

 

         ObjType GetSuperType(){

              return Type;

         }

 

         bool CanConvertTo( VClassType* pType ){

              if( this==pType )

                   return true;

              else if( Super!=NULL )

                   return Super->CanConvertTo(pType);

              else

                   return false;              

         }

 

         bool CanConvertTo( const vIID& id ){

              if( this->ClassID==id )

                   return true;

              else if( Super!=NULL )

                   return Super->CanConvertTo(id);

              else

                   return false;              

         }

 

         //可以根据ClassType的类型得到是否ReflectObject,如果是可以启动运行时信息

         //如果不是至少可以通过IsPointer检查是否是一个指针

         template<class type>

         type& GetMember(void* pHostObj , int i){

              return (reinterpret_cast<type*>(reinterpret_cast<UINT_PTR>(pHostObj) + Members[i].Offset))[0];

         }

     };

 

     struct VMethodBase

     {

         struct ParamType

         {

              typedef VClassType*    VClassTypePtr;

              VClassType*            ClassType;

              BOOL               IsConst;

              BOOL               IsPointer;

              BOOL               IsRefer;

 

              operator VClassTypePtr (){

                   return ClassType;

              }

         };

         VMethodBase()

              : IsConstructor(false)

         {

             

         }

         ~VMethodBase()

         {

              for( size_t i=0 ; i<Attrs.size() ; i++ )

              {

                   delete Attrs[i];

              }

              Attrs.clear();

         }

         typedef void ( VReflectBase::*FunAdress )();

         VString                Name;

         FunAdress          Address;

         bool               IsConstructor;

 

         ParamType                   ReturnType;

         std::vector<ParamType>      ArgTypes;

 

         std::vector<VAttribute*>    Attrs;

 

         VFX_API virtual ObjBase* Invoke( VReflectBase* pBindObject , std::vector<ObjBase*>& pArgs ) = 0;

         VFX_API virtual void* Constructor( std::vector<ObjBase*>& pArgs ) = 0;

     };

     定义的数据结构,VClassType内包含了他所有的成员变量VMember,所有的成员函数VMethodBase,这些数据结构定义很简单,看名字就能明白意思。现在我们最大的问题是如果获得这些数据。类型信息VclassTypeC++中的Class应该是一一对应的关系,我们应该在系统起来后,为每一个类产生他的所有详细信息。我的方法很简单,利用一个哑元全局变量构造的过程来实现信息自动注册,下面是应用的Example

struct DummyCreator

{

     DummyCreator(){

         BUILD_CASS(float);

         BUILD_CASS(double);

         BUILD_CASS(INT8);

         BUILD_CASS(INT16);

         BUILD_CASS(INT32);

         BUILD_CASS(INT64);

         BUILD_CASS(UINT8);

         BUILD_CASS(UINT16);

         BUILD_CASS(UINT32);

         BUILD_CASS(UINT64);

 

         BUILD_CASS(VIUnknown);

         BUILD_CASS(VObject);

         BUILD_CASS(VIObject);

 

         BUILD_CASS(reflect::VAttribute);

         BUILD_CASS(VStringA);

         BUILD_CASS(VStringW);

         BUILD_CASS(vfx::vgc::GCObject);

     }

} dummy_obj;

     BUID_CLASS是一条宏,他会在dummy_obj构造的时候根据传入的类型,获取指定类型的类型信息并且完成自动注册。因此现在最大的问题就是如果在BUILD_CLASS中实现类信息获取。

结构

         整个victoryreflection由几个文件组成,由于采用template+macro实现,主体代码在几个h文件内,这里简单列出他们并且进行一下介绍性描述

         vfxTemplateUtil.h:要用到的一些基础辅助模板代码

vfxReflectBase.h:类型信息的数据结构定义,Reflection系统下用到的一些特有特化版本模板实现。

vfxReflectionMethod.h:成员函数信息获取和调用,另外定义了一大堆macro,形成语法糖,方便实际运用。

vfxVictoryCoreAssembly.h:核心类型信息收集,cpp内还有一些测试函数。

Reflection设计的核心和难点就在于成员函数的处理。我的思想就是利用Functor来萃取函数信息,并且方便的调用它们。

技巧

         Reflection的过程,需要编译器做大量编译时期分析,所以要用到很多稀奇古怪的template,有的甚至是一些生僻的C++语法。对于template的一些技巧,vfxTemplateUtil.h里面都有可读性尚好的实现(还是不太好读,毕竟C++template代码本身就是很BT,我这里说的可读性是相对于BoostLoki一类至尊代码)。

1.  Typelist:这个几句话描述清楚真的很难,他的作用是让编译器在编译时期产生一个数组,这个数组装的是类型。

2.  Functor:仿函数,这玩艺的作用是把一个全局或者成员函数包装起来,

3.  萃取:TypeTraitsIsAbstract等等

Typelist是我Functor的基础,一个Functor,可调用体,参数个数是不定的,为了解决在template实现特化,Typelist可以用一个编译器类型包含多个参数类型,比较完美的解决参数个数变化的问题,ConstructorTraitsFunctor都利用了这个特性。而Functor中用到的SafeTListIndexOfTypelistTListIndexOf的一个增强,他利用了Selector原理,实现了Typelist下标访问越界的安全处理,他的实现,依托于TlistLength,这几个东西,都是依赖于编译期递归。如果Typelist内包含的类型个数不足,编译器将返回NullObject这个自定义的非法类型。

Reflection一个重要的特性对象构造,需要获得对象构造过程,以获得动态对象创建能力,比如ClassFactory设计。C++new Type(args)语法可以分配对象,并且调用对应版本构造函数,构造函数也是一个函数,理论上他也是一个地址,可以call,但是它没有返回值,所以typedef (Classname::*ClassName)(