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