運行期構建C++類型系統

jopen 10年前發布 | 9K 次閱讀 C/C++開發 C/C++

現代高級的面向對象語言(如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);
         }
}

 

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!