cpp小菜園

簡單即是複雜

  C++博客 :: 首页 :: 联系 :: 聚合  :: 管理
  2 Posts :: 0 Stories :: 0 Comments :: 0 Trackbacks

常用链接

留言簿(3)

我参与的团队

搜索

  •  

最新评论

阅读排行榜

评论排行榜


  最近(其实也有一段时间了),在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)
};

  好吧,如果说到实际应用的问题,洒家确实没有考虑太多,反正测试函数能跑,结果也对了,洒家也就满足了,就单纯的当做是一次实验,或者是学习而已。
posted on 2012-04-25 13:18 阅读(4014) 评论(0)  编辑 收藏 引用

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   博问   Chat2DB   管理