運行期構建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); } }