最近(其實也有一段時間了),在c++項目中需要用到類似java的反射機制,時間也比較趕,只能周圍看看有沒有現成的庫可用了,找到的資料也不算少,但多數都是關于動態創建類的,有點像工廠模式,而我需要的,是把實體類中的屬性反射出來。最后,找到了一個叫LibReflection的東西,雖然后來還是沒用上這個,但是稍微看了一下代碼,還是有不少收獲,所以就在這里記錄下來了,當做學習,也當做對這個庫的一個分析吧。
LibReflection是屬性和函數反射都有實現的,而整個庫竟然就一個h文件,本人精力有限,只把屬性反射抽取出來,相信離實際應用也有距離,如果有人路過看到,請體諒別拍磚了。
那么,首先看一下使用:
TestClass inst;
tat::tat_class *test_class = TestClass::get_class_ptr();
std::map<std::string,tat::tat_field> field_map = test_class->get_fields();
tat::tat_field test_vec_field = field_map.find("_vec_f")->second;
std::vector<int> vec;
test_vec_field.get(&inst,vec);
vec.push_back(22);
test_vec_field.set(&inst,vec);
std::cout<<inst._vec_f[0]<<std::endl;
TestClass類先不給出,反正就是該類有一個名字為“_vec_f”的屬性,類型是std::vector<int>,然后這段代碼通過反射修改_vec_f的內容。。至于tat_class和tat_field,相信熟悉java的人都能猜出是什么了。
獲取及設置一個實例的成員變量的值不是難事,用一個宏就可以了:
#define _OFFSET_(_Obj_Ty,_Key) \
((unsigned long)(&((_Obj_Ty *)0)->_Key))
這個宏應該也不難理解,就是獲取成員變量相對實例指針的偏移值。而關鍵問題是,如何通過變量名稱就能查找出這個偏移呢?如何在變量初始化的時候就能記錄下這個偏移呢?我們可以通過宏來聲明變量,那么聲明的時候就可以做一些事情了,但具體做些什么,還是沒頭緒。
這個問題自己思考了一段時間,無果。然后看了一下實現,深感自己的基礎還沒過關,其實說白了就是兩個東西:構造函數、靜態成員變量。
我們的目標是很明確的,要做到java的效果,結合上面那個宏,也不難想象出Class和Field類的樣子(這里是tat_class和tat_field),tat_class無非就是保存了一個map,key是屬性的名稱,value是tat_field,至于tat_field就簡單了,核心就是保存內存的偏移值,再提供操作值的接口就可以了。
先來看看tat_class:
class tat_class
{
private:
std::map<std::string,tat_field> _field_map;
std::string _key;
public:
std::map<std::string,tat_field> get_fields()
{
return this->_field_map;
}
tat_field get_field(std::string key)
{
std::map<std::string,tat_field>::iterator itr = _field_map.find(key);
return (*itr).second;
}
void add_field(const tat_field &field)
{
_field_map.insert(std::pair<std::string,tat_field>(field.get_key(),field));
}
};
沒有什么理解上的難點吧。
然后看看tat_field:
class tat_field
{
private:
unsigned long _offset;
std::string _key;
public:
tat_field(unsigned long offset,std::string key):_offset(offset),_key(key){}
tat_field(const tat_field &field)
{
this->_offset = field._offset;
this->_key = field._key;
}
public:
template<typename _Obj_Ty,typename _Value_Ty>
void get(_Obj_Ty *obj,_Value_Ty &value)
{
value = *((_Value_Ty *)((unsigned char *)obj + _offset));
}
template<typename _Obj_Ty,typename _Value_Ty>
void set(_Obj_Ty *obj,const _Value_Ty &value)
{
*((_Value_Ty *)((unsigned char *)obj + _offset)) = value;
}
std::string get_key() const
{
return this->_key;
}
};
變量值的get和set函數使用了模板,這是為了使用的時候能作簡單的類型推斷。
然后就是重點了,如何在聲明類的時候就把tat_class注入呢?如前面說的,靜態成員變量:
#define CLASS_REGISTER(_Obj_Ty) \
public: \
static tat::tat_class * get_class_ptr() \
{ \
static tat::tat_class __class_##_Obj_Key##__; \
return &__class_##_Obj_Key##__; \
}
這個宏做了兩件事:第一,聲明靜態函數get_class_ptr(),返回tat_class類型,第二,當然就是初始化tat_class了,因為同樣是靜態,所以不同的實例就共享了。
接下來是另一個重點,如何在聲明成員變量的時候把tat_field注入到tat_class中,還是用宏解決:
#define FIELD_REGISTER(_Access,_Field_Ty,_Field_Key,_Obj_Ty) \
_Access: \
_Field_Ty _Field_Key; \
private: \
class __field_register_##_Field_Key##__ \
{ \
public: \
__field_register_##_Field_Key##__() \
{ \
static tat::__field_register__ reg_##_Field_Key( \
_Obj_Ty::get_class_ptr(), \
_OFFSET_(_Obj_Ty,_Field_Key), \
#_Field_Key); \
} \
}_Field_Key##_register;
分析這個宏,首先當然是聲明變量了;然后就是一個神秘的類:__field_register_##_Field_Key##__
,這個類就只有一個構造函數,構造函數里面又是一個靜態變量,類型是tat::__field_register__,可以想象得到,既然tat_class已經可以聲明出來了,tat_field自然也沒有難度,但聲明是不足夠的,還需要放到tat_class的map里面才算完成,在聲明一個變量的時候,能做事的地方,我只能想到是構造函數了,所以就有了剛剛那個神秘的類,那個類的唯一作用就是其構造函數,而構造函數里面聲明靜態變量,則是防止多次實例化類帶來的冗余數據,確保一個成員變量只有一個tat_field;
剩下的關鍵,就是tat::__field_register__了:
class __field_register__
{
public:
__field_register__(tat_class *class_ptr,unsigned long offset,std::string key)
{
tat_field field(offset,key);
class_ptr->add_field(field);
}
};
沒有什么神秘的地方,這個東西就這么完成了。
差點忘了TestClass的聲明:
#include "fieldref.h"
class TestClass
{
public:
TestClass(void);
~TestClass(void);
CLASS_REGISTER(TestClass)
FIELD_REGISTER(public,long,_long_f,TestClass)
FIELD_REGISTER(public,int,_int_f,TestClass)
FIELD_REGISTER(public,std::string,_str_f,TestClass)
FIELD_REGISTER(public,std::vector<int>,_vec_f,TestClass)
};
好吧,如果說到實際應用的問題,灑家確實沒有考慮太多,反正測試函數能跑,結果也對了,灑家也就滿足了,就單純的當做是一次實驗,或者是學習而已。