运行期构建C++类型系统
现代高级的面向对象语言(如Java、C#等)一般会提供一种称之为“反射”的特性,通过它可以动态的创建类型实例,将类型绑定到现有对象,或从现有对象中获取类型,还可以调用类型的方法及访问其字段和属性。“反射”的功能非常强大,其中一种比较重要的应用是将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字节,其中从头到尾变量依次是mdisp、pdisp、vdisp,这三个变量每个占用了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); } }