C++ 反射

来源:互联网 发布:命名空间的含义php 编辑:程序博客网 时间:2024/05/22 00:07

参考一

C++的反射

     C++语言本身是不支持反射的,但实际应用中总是会有将对象序列化的需求,总不可能C++不支持,我们就不用C++了,既然发明C++的大师们没有考虑这个,那我们只有自己动手了,毛主席说过“自己动手,丰衣足食”!

天生限制

C++语言本身不支持反射机制,但C++对象总是要序列化的,序列化就是存储到磁盘上,将对象变成一定格式的二进制编码,然后要用的时候再将保存在磁盘上的二进制编码转化成一个内存中的对象,这个过程中总是需要有一个指示来告诉编译器要生成什么样的对象,最简单的方式当然就是类名了,例如:将一个ClassXXX对象存储到磁盘上,再从磁盘读取的时候让编译器根据“ClassXXX”名称来new一个对象。

但是问题出现了,C++语言本身不支持反射,也就是说不能通过如下方式生成一个对象:

ClassXXX object = new “ClassXXX”;

工厂方法

当然,这样的方法不行,那我们只有另辟蹊径。最简单的就是工厂方法了:

ClassXXX* object = FactoryCreate(“ClassXXX”);

至于FactoryCreate的设计就很简单了,if的集合就可以了:

if(name = “ClassXXX”)

return new ClassXXX;

if(name = “ClassYYY”)

return new ClassYYY;

看起来不错,来个类名就可以生成对应的对象,功能上解决了根据类名生成对象的问题。

假如以上所有的代码都有你一个人编写,那当然问题不大,但是假如有一天你的公司扩大了,这部分代码由两个不同的组A和B来维护,啊哈,问题来了,A组每添加或者修改一个类,都要通知B组更新FactoryCreate函数,也就是说A组的任何关于类的修改,都需要B组来修改,但实际上B的修改不产生任何价值,而且不胜其烦,永无止尽!!如果哪天来了一个新员工,由于对这个规定还不清楚,忘记了通知,那就完了:编译通不过!

一个公司内都会产生如此多的问题,更何况微软这样的大公司是面对全球的各种各样的客户,如果微软把这部分做进框架代码中,呵呵,那微软所有的人不用干其他事情了,每天处理来自全球的要求修改FactoryCreate函数的邮件和电话就够他们忙的了:)

回调工厂

既然此路不好走,那么我们再考虑其它方法吧,一个可选的方法是将FactoryCreate做成回调函数,框架提供注册接口RegisterFactoryCreate,框架函数如此实现:

typedef CObject* (*FactoryCreate_PTR)(String name);

RegisterFactoryCreate(FactoryCreate_PTR fc_ptr);

应用代码如此实现:

CObject* MyFactoryCreate(String name);

RegisterFactoryCreate(MyFactoryCreate);

到这里一个框架和应用分离的反射机制基本实现了,你是否长吁一口气,然后准备泡杯咖啡,稍微放松一下呢?确实可以稍微休息一下了,毕竟我们完成了一件非常了不起的事情,让C++实现了反射。

但你只悠闲了一两天,麻烦事就来了。员工张三跑来向你抱怨“老大,李四注册的反射函数把我的覆盖了”!哦,你仔细一看,My god,这个注册函数只能注册一个反射函数,后注册的就把前面的覆盖了!

怎么办?总不可能又要求所有的类的反射函数都在一个工厂里实现吧,那这样就又回到了工厂方法中描述的时代了。

当然,聪明的你估计很快就能想出问题的解决方法,将RegisterFactoryCreate函数稍加修改就能满足要求了,新的实现如下:

RegisterFactoryCreate(FactoryCreate_PTR fc_ptr,String className)

然后要求每个类都单独写自己的FactoryCreate_PTR函数,类似如下方式:

static CObject* ClassXXX::CreateClassXXX (){

       return new ClassXXX;

};

static CObject* ClassYYY::CreateClassYYY(){

       return new ClassYYY;

};

到此为此终于大功告成,通过我们的智慧实现了C++的反射功能!一股自豪感油然升起:)

最后的杀手锏:宏

当你为自己的聪明才智而骄傲的时候,那边却有几个开发的兄弟在发出抱怨“唉,这么多相似的函数,看着都眼花,每个类都要写,烦死了”。

或者有一天,你要在每个类的CreateClass函数中增加一个其它功能(例如日志),那么开发的兄弟真的是要烦“死了”!!!

其实仔细一看,包括函数申明、函数定义、函数注册,每个类的代码除了类名外其它都是一模一样的,有没有简单的方法呢?

肯定是有的,这个方法就是宏了,按照如下方法定义宏:

#define DECLARE_CLASS_CREATE(class_name) \

static CObject* CreateClass## class_name ();

#define IMPL_CLASS_CREATE(class_name) \

static CObject* CreateClass## class_name (){ \

       return new class_name;             \

};

#define REG_CLASS_CREATE(class_name) \

RegisterFactoryCreate(class_name::CreateClass## class_name, #class_name);

注:##是连接符,将两个字符串连接起来,#是将class_name作为字符串处理。

大家可以比较一下,用了宏和不用宏是不是代码感觉完全不一样呢?而且那天需要增加一个简单的功能,只需要改宏定义就ok了,不要全文搜索所有相关函数,然后一个一个的重复添加。

到这里才真正是大功告成!!

后记

某天分析Spring的IOC时,看到Digester最后利用的实际上是Java的反射机制来根据XML文件定义生成Java对象,突发奇想:如果是C++该怎么办?

于是自己就开始分析起来,分析了一段时间突然想起微软的MFC不正是要支持C++对象序列化的吗?

赶紧打开深入浅出MFC,重新将这部分研究了一下。看到微软的天才们在MFC中用宏来实现RTTI、Dynamic Create、Seralize功能时,我反过来思考“如果是我,我会如何设计?”、“为什么会这么设计”?然后一一分析这些各种可能的实现方式,一步一步的推导,最后发现竟然自然而然的就推出了MFC的这种实现方式!

 

参考二

c++实现反射机制(两篇) 

                                                 第一篇

C++ 编程语言是一款功能强大的计算机应用语言。其能够支持很多程序设计风格。我们今天将会在这里为大家详细介绍一下有关C++反射机制的具体实现步骤,大家可以从中获得一些有帮助的内容。

在Java编程中,我们经常要用到反射,通过反射机制实现在配置文件中的灵活配置, 但在C++编程中,对这种方式步提供现有的支持,那么我们怎么才能在配置文件中配置想要调用的对象呢? 

我们的思路是通过对象名称确定对象实例,把对象名和对象实例通过哈希表进行映射,那么我们就可以实现通过对象名称获取对象了。首先定义一个C++反射机制的结构:

 

Cpp代码 复制代码 收藏代码
  1. struct ClassInfo     
  2. {     
  3. public:     
  4. string Type;     
  5. funCreateObject Fun;     
  6. ClassInfo(string type, funCreateObject fun)     
  7. {     
  8. Type = type;     
  9. Fun = fun;     
  10. Register(this);     
  11. }     
  12. };   

 

其中Register这样定义

  1. bool Register(ClassInfo* ci);

 

然后定义一个类,头文件如下:

Cpp代码 复制代码 收藏代码
  1. class AFX_EXT_CLASS DynBase      
  2. {     
  3. public:     
  4. DynBase();     
  5. virtual ~DynBase();     
  6. public:      
  7. static bool Register(ClassInfo* classInfo);     
  8. static DynBase* CreateObject(string type);     
  9. private:      
  10. static std::map<string,ClassInfo*> m_classInfoMap;     
  11. };   

cpp文件如下:

Cpp代码 复制代码 收藏代码
  1. std::map< string,ClassInfo*> DynBase::m_classInfoMap =    
  2. std::map< string,ClassInfo*>();     
  3. DynBase::DynBase()     
  4. {     
  5. }     
  6. DynBase::~DynBase()     
  7. {     
  8. }     
  9. bool DynBase::Register(ClassInfo* classInfo)     
  10. {      
  11. m_classInfoMap[classInfo->Type] = classInfo;      
  12. return true;      
  13. }     
  14. DynBase* DynBase::CreateObject(string type)     
  15. {     
  16. if ( m_classInfoMap[type] != NULL )      
  17. {      
  18. return m_classInfoMap[type]->Fun();     
  19. }     
  20. return NULL;     
  21. }   

 那么我们在C++反射机制的操作中实现映射的类只要继承于DynBase就可以了,比如由一个类CIndustryOperate

头文件如下:

Cpp代码 复制代码 收藏代码
  1. class CIndustryOperate : public DynBase     
  2. {     
  3. public:     
  4. CIndustryOperate();     
  5. virtual ~CIndustryOperate();     
  6. static DynBase* CreateObject(){return new CIndustryOperate();}     
  7. private:     
  8. static ClassInfo* m_cInfo;     
  9. };   

cpp文件如下:

Cpp代码 复制代码 收藏代码
  1. ClassInfo* CIndustryOperate::m_cInfo = new ClassInfo   
  2. ("IndustryOperate",(funCreateObject)( CIndustryOperate::   
  3. CreateObject));     
  4. CIndustryOperate::CIndustryOperate()     
  5. {     
  6. }     
  7. CIndustryOperate::~CIndustryOperate()     
  8. {     
  9. }   

 

                                                                        第二篇

       C++语言本身不支持反射机制,但C++对象总是要序列化的,序列化就是存储到磁盘上,将对象变成一定格式的二进制编码,然后要用的时候再将保存在磁盘上的二进制编码转化成一个内存中的对象,这个过程中总是需要有一个指示来告诉编译器要生成什么样的对象,最简单的方式当然就是类名了,例如:将一个 ClassXXX对象存储到磁盘上,再从磁盘读取的时候让编译器根据“ClassXXX”名称来new一个对象。
但是问题出现了,C++语言本身不支持反射,也就是说不能通过如下方式生成一个对象:
ClassXXX object = new “ClassXXX”;

工厂方法
当然,这样的方法不行,那我们只有另辟蹊径。最简单的就是工厂方法了:
ClassXXX* object = FactoryCreate(“ClassXXX”);
至于FactoryCreate的设计就很简单了,if的集合就可以了:
if(name = “ClassXXX”)
return new ClassXXX;
if(name = “ClassYYY”)
return new ClassYYY;

看起来不错,来个类名就可以生成对应的对象,功能上解决了根据类名生成对象的问题。
假如以上所有的代码都有你一个人编写,那当然问题不大,但是假如有一天你的公司扩大了,这部分代码由两个不同的组A和B来维护,啊哈,问题来了,A组每添加或者修改一个类,都要通知B组更新FactoryCreate函数,也就是说A组的任何关于类的修改,都需要B组来修改,但实际上B的修改不产生任何价值,而且不胜其烦,永无止尽!!如果哪天来了一个新员工,由于对这个规定还不清楚,忘记了通知,那就完了:编译通不过!
一个公司内都会产生如此多的问题,更何况微软这样的大公司是面对全球的各种各样的客户,如果微软把这部分做进框架代码中,呵呵,那微软所有的人不用干其他事情了,每天处理来自全球的要求修改FactoryCreate函数的邮件和电话就够他们忙的了:)

回调工厂
既然此路不好走,那么我们再考虑其它方法吧,一个可选的方法是将FactoryCreate做成回调函数,框架提供注册接口RegisterFactoryCreate,框架函数如此实现:
typedef CObject* (*FactoryCreate_PTR)(String name);
RegisterFactoryCreate(FactoryCreate_PTR fc_ptr);
应用代码如此实现:
CObject* MyFactoryCreate(String name);
RegisterFactoryCreate(MyFactoryCreate);
到这里一个框架和应用分离的反射机制基本实现了,你是否长吁一口气,然后准备泡杯咖啡,稍微放松一下呢?确实可以稍微休息一下了,毕竟我们完成了一件非常了不起的事情,让C++实现了反射。

但你只悠闲了一两天,麻烦事就来了。员工张三跑来向你抱怨“老大,李四注册的反射函数把我的覆盖了”!哦,你仔细一看,My god,这个注册函数只能注册一个反射函数,后注册的就把前面的覆盖了!
怎么办?总不可能又要求所有的类的反射函数都在一个工厂里实现吧,那这样就又回到了工厂方法中描述的时代了。
当然,聪明的你估计很快就能想出问题的解决方法,将RegisterFactoryCreate函数稍加修改就能满足要求了,新的实现如下:
RegisterFactoryCreate(FactoryCreate_PTR fc_ptr,String className)
然后要求每个类都单独写自己的FactoryCreate_PTR函数,类似如下方式:
static CObject* ClassXXX::CreateClassXXX (){
       return new ClassXXX;
};

static CObject* ClassYYY::CreateClassYYY(){
       return new ClassYYY;
};

到此为此终于大功告成,通过我们的智慧实现了C++的反射功能!一股自豪感油然升起:)

最后的杀手锏:宏
当你为自己的聪明才智而骄傲的时候,那边却有几个开发的兄弟在发出抱怨“唉,这么多相似的函数,看着都眼花,每个类都要写,烦死了”。
或者有一天,你要在每个类的CreateClass函数中增加一个其它功能(例如日志),那么开发的兄弟真的是要烦“死了”!!!

其实仔细一看,包括函数申明、函数定义、函数注册,每个类的代码除了类名外其它都是一模一样的,有没有简单的方法呢?
肯定是有的,这个方法就是宏了,按照如下方法定义宏:
#define DECLARE_CLASS_CREATE(class_name) \
static CObject* CreateClass## class_name ();

#define IMPL_CLASS_CREATE(class_name) \
static CObject* CreateClass## class_name (){  \
       return new class_name;             \
};

#define REG_CLASS_CREATE(class_name) \
RegisterFactoryCreate(class_name::CreateClass## class_name, #class_name);
注:##是连接符,将两个字符串连接起来,#是将class_name作为字符串处理。

大家可以比较一下,用了宏和不用宏是不是代码感觉完全不一样呢?而且那天需要增加一个简单的功能,只需要改宏定义就ok了,不要全文搜索所有相关函数,然后一个一个的重复添加。

到这里才真正是大功告成!!

 

 

参考三

C++实现Reflection

最近1年多,我一直在琢磨和大量使用cli来开发游戏项目,.net系统的反射系统给我
的开发设计思路,带来了很大的冲击。反射真的可以从根本上,让设计思路相对传统
开发模式有很大的改变。
但是反射通常都提供在类似java等动态语言上,后来ms在.net平台上推出了一个很强
大完善的反射机制,我最近1年多的项目都是大量使用他,为我的项目开发,做出了很
多有意思的东西。但是我的victory3d的core因为效率原因,依然是采用纯C++开发,
享受不到.net平台的便利。不少人在呼吁C++标准组织,给C++添加上反射特性,但是
据我所知C++0x规范目前也就能搞一个gc什么的,对于reflection的支持,还不知道要
什么时候。
于是,我决定自己尝试实现一下在c++中,实现reflection,经过2天试验,目前基本
达成实际目标,下面是一个我的c++反射一个class的例子:

 ///<ReflectExport name="MyTestClass"/>
 class testclass : public VReflectBase
 {
 public:
  ///<ReflectExport/>
  void fun0()
  {
   return;
  }
  ///<ReflectExport/>
  int fun1(float a)
  {
   return 0;
  };
 };

 ///<ReflectExport name="MyTestClass"/>
 class testclass2 : public testclass
 {
 public:
  ///<ReflectExport/>
  float fun3(int a)
  {
   return 0;
  };
 };

 DEF_REFLECT(reflect::testclass,NullObject)

 BEGIN_REFLECT_METHOD(reflect::testclass)
  REF_METHOD0(reflect::testclass,fun0,void)
  REF_METHOD1(reflect::testclass,fun1,int,float)
 END_REFLECT_METHOD

 DEF_REFLECT(reflect::testclass2,testclass)

 BEGIN_REFLECT_METHOD(reflect::testclass2)
  REF_METHOD1(reflect::testclass2,fun3,float,int)
 END_REFLECT_METHOD

 void reflect_test()
 {
  //创建ClassType信息
  ClassExporter<reflect::testclass>::BuildClass();
  ClassExporter<reflect::testclass2>::BuildClass();

  ClassExporter<reflect::testclass>::BuildMethod();
  ClassExporter<reflect::testclass2>::BuildMethod();

  //获得testclass的ClassType
  VClassType* pp = ClassExporter<reflect::testclass>::ClassType;
  VClassType* pp2 = ClassExporter<reflect::testclass2>::ClassType;

  testclass obj;

  std::vector<ObjBase*> args;
  pp->Methods[0]->Invoke( &obj , args );

  ObjBase arg1;
  arg1.f = 1.2f;
  args.push_back(&arg1);
  pp->Methods[1]->Invoke( &obj , args );
 }

testclass和testclass2两个类,通过两段宏,就可以把反射信息倒出来,最后,通过一个
template ClassExporter类来操作classType,整体看起来,使用方便性方面和.net的基本
接近,倒出过程因为我是运行中获得,cli是编译器获得,显得复杂一点,不过这个,我们
可以通过外部工具,扫描倒出类的申明来自动产生。也就不是什么工作量了。
哈哈,以后只要我愿意,我甚至可以让自己的脚本随意call我victory3d最核心的c++任何
方法了。c++ reflection我再完善完善,就集成在victory中,并且广泛使用了,到时候,
我会将reflection部分代码完整的贴在blog,和大家一起讨论。 

 

参考四

在C++实现反射

反射这个特性在C++中是没有的。所谓反射,自己的认为就是通过一个名字就可创建、调用、获取信息等一系列的操作,这个在脚本语言里面是比较常见的,COM组件也类似,知道个ID名,就可以做很多的工作了。

看看JAVA中的描述:

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法;这种动态获取的信息以及动态调用对象的方法的功能称为JAVA语言的反射机制。

JAVA反射机制主要提供了以下功能: 在运行时判断任意一个对象所属的类;在运行时构造任意一个类的对象;在运行时判断任意一个类所具有的成员变量和方法;在运行时调用任意一个对象的方法;生成动态代理。

反射发挥威力的场所:1.序列化(Serialization)和数据绑定(Data Binding)。2.远程方法调用(Remote Method Invocation RMI)。3.对象/关系数据映射(E/R Mapping)。

虽然没有,但既然我们用的是C++,总是会有办法的~~

ITEM1:动态创建

在实现反射的过程中的第一步要做的可能就是动态创建了,就是用一个类的名字就可以得到这个类的创建函数,从而进行对象的创建。要实现这些,首先对于要反射的类得提供一个能创建自己的回调函数,我们可以使用这个函数来创建这个类的对象,这样,也使得创建活动内聚,减少将来不必要的修改。同时,为了我们可以通过类名获取刚才所说的回调函数,我们就需要一种全局储存结构来保存创建函数和对应类名的映射关系。

这个回调函数就可以是这样的:

static CBaseClass* ClassName::CreateClassName (){

              return new ClassName;

};

在MFC中,我们常使用DECLARE_DYNCREATE宏,这个宏可以使每个CObject的派生类的对象具有运行时动态创建的能力。那么我们也可以使用宏来简化上面实现函数的方法,如下:

#define DEFINE_DYN _CREATE(class_name) /

static CBaseClass * CreateClass## class_name ();

 

#define IMPLIMENT_DYN _CREATE(class_name) /

static CBaseClass * CreateClass## class_name (){  /

       return new class_name;             /

};

分别放入需要创建的类的H文件和CPP文件就可以了~,当然,仅仅这样还不可以,因为我们不但要可以低耦合地创建对象,还需要把对象的创建方法和类名的映射存储起来,那么我们就还需要一个宏:

#define REGISTER_CLASS (class_name) /

RegisterFactory (class_name::CreateClass## class_name, #class_name);

在RegisterFactory函数中,我们需要一种结构,比如Map,将对应关系存储起来,以备以后的使用。

当然,我们也可以使用另一种方法,我们可以创建一个一般类,这个类可以进行映射关系的存储,提取,运行,或者动态映射的功能,我们让我们的反射类继承这个一般类就可以了。还是见代码吧(在2008下使用),如下所示:

(援引自http://blog.csdn.net/wrq147/archive/2010/05/18/5603262.aspx

#include <string>  #include <map>  #include <iostream>using namespace std;typedef void* (*CreateFuntion)(void);class ClassFactory{public:        static void* GetClassByName(std::string name)        {            std::map<std::string,CreateFuntion>::const_iterator find;                find = m_clsMap.find(name);                if(find==m_clsMap.end())                {                        return NULL;                }                else                {                        return find->second();                }        }        static void RegistClass(std::string name,CreateFuntion method)        {                m_clsMap.insert(std::make_pair(name,method));        }private:        static std::map<std::string,CreateFuntion> m_clsMap;}; std::map<std::string, CreateFuntion> ClassFactory::m_clsMap;  class RegistyClass{public:        RegistyClass(std::string name, CreateFuntion method)        {                ClassFactory::RegistClass(name, method);        }};template<class T, const char name[]>class Register{public:        Register()        {                const RegistyClass tmp=rc;        }        static void* CreateInstance()        {                return new T;        }public:        static const RegistyClass rc;}; template<class T,const char name[]>const RegistyClass Register<T,name>::rc(name, Register<T, name>::CreateInstance); #define DEFINE_CLASS(class_name) /char NameArray[]=#class_name;/class class_name:public Register<class_name, NameArray> #define DEFINE_CLASS_EX(class_name,father_class) /char NameArray[]=#class_name;/class class_name:public Register<class_name, NameArray>,public father_class DEFINE_CLASS(CG){public:        void Display()        {                printf("I am Here/n");        }}; int main(int tt){                CG* tmp=(CG*)ClassFactory::GetClassByName("CG");        tmp->Display();        return 0;}


 

ITEM2:动态识别

所谓的RTTI(Run-Time Type Information)也就是运行时类型检查,在C++中的自有动态技术应该就是typeiddynamic_cast了,找到一段typeid的例程,如下:

#include <string.h>
#include <iostream.h>
#include <typeinfo.h>
#include <stdio.h>

 

class Base
{
public:
              Base()
              {

                            strcpy(name,"Base");

              }

             

              virtual void display()

              {

                            cout<<"Display Base."<<endl;

              }

protected:

              char name[64];

};

 

class Child1:public Base

{

public:

              Child1()

              {

                            strcpy(name,"Child1");

              }

              void display()
              {

                            cout<<"Display Child1."<<endl;

              }

};

class Child2:public Base
{
public:
              Child2()
              {

                            strcpy(name,"Child2");

              }

              void display()

              {

                            cout<<"Display Child2."<<endl;

              }

};

void Process(Base *type)
{

              if( (typeid(Child1)) == (typeid(*type)) )
              {

                            ((Child1*)type)->display();
              }

              else if( (typeid(Child2)) == (typeid(*type)) )

              {
                            ((Child2*)type)->display();
              }
              else
              {
                            cout<<"Unknow type!"<<endl;
              }

}

int main(void)
{
              Base *pT1 = new Child1();
              Base *pT2 = new Child2();
              Process(pT1);
              Process(pT2);

              printf("OK/n");
              return 0;

}

注意:
1.#include "typeinfo.h"
2.
编译选项/GR setting ->c++ -> c++ language -> Enable Run-Time Type Infomation
3.typeid的使用

 

ITEM3C++反射技术

找到了下面的介绍,看看成型的操作吧

Lit Window Library

来自http://sourceforge.net/projects/litwindow大概看了一下,使用了宏建立了一个比较复杂的数据结构,用来提供成员反查以及成员访问。

tinybind

来自http://sourceforge.net/projects/tinybind/应该不算是反射,不了定一个Binding函数实现XMLElement定,自动实现XML<->class对实现C++反射也有一定的参考作用。

OODBC

来自http://www.garret.ru/~knizhnik/cpp.html这个也应该不算是反射,也是通过宏建立一个记录成员变量在类中的偏移位置的链表。自动实现数据库字段与成员的绑定,有点Hibernate的味道。呵呵。同时,作者提供的另外几个例如FastDBGigaBasegoodsPOST++都使用了类似的技术。

 

很多都需要你自己慢慢的思考,也许这就是CPP魅力

反射,To be, or not to be...

 

原创粉丝点击