运行期构建C++类型系统

jopen 10年前

现代高级的面向对象语言(如JavaC#等)一般会提供一种称之为“反射”的特性,通过它可以动态的创建类型实例,将类型绑定到现有对象,或从现有对象中获取类型,还可以调用类型的方法及访问其字段和属性。“反射”的功能非常强大,其中一种比较重要的应用是将JSON字符串直接映射成某个类型的变量。

         相对上面提到的那些面向对象语言而言,同样是面向对象的C++语言的类型系统功能还是比较弱的,当然这也有其这么做的原因。那么有没有办法在现有的C++类型系统上提供一些有益的补充呢?本文试着给出一些答案。

         如果您使用过VC编译器,那么您也许可以尝试一下cl命令“/d1 reportAllClassLayout”,它可以在编译期打印类型的内存结构,例如下面这个展示:

1>  class _PMD size(12):

1>   +---

1>   0       | mdisp

1>   4       | pdisp

1>   8       | vdisp

1>   +---

上面展示的这个类型是_PMD,内存大小为12字节,其中从头到尾变量依次是mdisppdispvdisp,这三个变量每个占用了4个字节。

         为了在运行期也能够的获取类型的内存信息,首先我们需要定义一个变量需要的信息结构:

/*   * 成员变量的元数据   */  struct FieldMetadata  {           // 成员变量的名称           string name;              // 成员变量的类型           string type;              // 成员变量的地址           size_t offset;              FieldMetadata(string name, string type, size_t offset)           {                     this->name = name;                     this->type = type;                     this->offset = offset;           }  };

         我们想一下,既然是类型的元数据信息,那么也就是与具体的类型实例是无关的,元数据信息应该属于类型本身,那么将元数据信息定义成static变量是再适合不过的了:

static vector<FieldMetadata> fieldinfo;

         每当给类型增加一个成员变量的时候,我们除了定义一个成员变量以外,还需要将它的类型和内存信息注册到元数据信息中:

         int a;  fieldinfo.add(FieldMetadata(“a”, “int”, 0));           int b;  fieldinfo.add(FieldMetadata(“b”, “int”, 4));           int c;  fieldinfo.add(FieldMetadata(“c”, “int”, 8));

         定义一个变量很容易,但是要自动执行一个函数却很难,下面我要讲的涉及到了C++模板,元编程,offsetof,宏定义等需要深厚的C++功底才能理解的内容,要是让我一步一步的讲的细致入微,我担心力有所不逮。如果您有深厚的C++功底,那么我接下来要讲的内容其实又很简单,您肯定能一看就懂。下面呈上我实作的代码。

   #include <vector>  #include <string>  using namespace std;        /*   * 成员变量的元数据   */  struct FieldMetadata  {           // 成员变量的名称           string name;              // 成员变量的类型           string type;              // 成员变量的地址           size_t offset;              FieldMetadata(string name, string type, size_t offset)           {                     this->name = name;                     this->type = type;                     this->offset = offset;           }  };        /*   * 声明结构体类型   */  #define Declare_Struct(class_type) \  private: \   \  typedef class_type this_class; \   \  template<int N> class Init_I \  { \  public: \           Init_I(vector<FieldMetadata>& metas) \           {} \  }; \        /*   * 定义结构体变量   */  #define Define_Field(var_index, var_type, var_name) \  public: \   \  var_type var_name; \   \  private: \   \  template<> class Init_I<var_index> \  { \  public: \           Init_I(vector<FieldMetadata>& metas) \           { \                     FieldMetadata fmd(#var_name, typeid(var_type).name(), offsetof(this_class, var_name)); \                     metas.insert(metas.begin(), fmd); \           } \  }; \        /*   * 定义结构体元数据   */  #define Define_Metadata(var_count) \  public: \   \  static vector<FieldMetadata> fieldinfo; \   \  private: \   \  template<int N> class CallInits \  { \  public: \           CallInits(vector<FieldMetadata>& metas) \           { \                     Init_I<N> in(metas); \                     CallInits<N-1> ci(metas); \           } \  }; \   \  template<> class CallInits<1> \  { \  public: \           CallInits(vector<FieldMetadata>& metas) \           { \                     Init_I<1> in(metas); \           } \  }; \   \  static vector<FieldMetadata> Init() \  { \           vector<FieldMetadata> fmd; \           CallInits<var_count> ci(fmd); \           return fmd; \  } \        /*   * 实现结构体类型   */  #define Implement_Struct(class_type) \  vector<FieldMetadata> class_type::fieldinfo = class_type::Init(); \        struct Test  {              Declare_Struct(Test);              Define_Field(1, int, a)              Define_Field(2, int, b)              Define_Field(3, int, c)              Define_Metadata(3)     };        Implement_Struct(Test)        void main()  {           printf("struct size : %d \n", sizeof(Test));              printf("fieldinfo size : %d \n", Test::fieldinfo.size());              printf("struct layout : \n");           for (auto iter = Test::fieldinfo.begin(); iter != Test::fieldinfo.end(); iter++)           {                     printf("%s, %s, %d \n", (*iter).name.c_str(), (*iter).type.c_str(), (*iter).offset);           }  }