【使用Boost的C++反射】(来源于stackoverflow回答翻译)

本文介绍了一种在C++中实现轻量级反射的方法,通过宏定义和元编程技术,创建了一个可以遍历和访问类字段的框架。这个框架包括了字段数量的获取、字段数据的结构以及使用访问者模式进行字段迭代。示例中展示了如何定义一个可反射的Person类,并通过反射机制打印出类的字段及其值。
摘要由CSDN通过智能技术生成

您需要做的是让预处理器生成有关字段的反射数据。此数据可以存储为嵌套类。

首先,为了使它更容易和更干净地在预处理器中编写它,我们将使用类型化表达式。类型化表达式只是将类型放在括号中的表达式。因此,您将编写(int) x,而不是写入int x。下面是一些方便的宏,可帮助使用类型化表达式:

#define REM(...) __VA_ARGS__
#define EAT(...)

// Retrieve the type
#define TYPEOF(x) DETAIL_TYPEOF(DETAIL_TYPEOF_PROBE x,)
#define DETAIL_TYPEOF(...) DETAIL_TYPEOF_HEAD(__VA_ARGS__)
#define DETAIL_TYPEOF_HEAD(x, ...) REM x
#define DETAIL_TYPEOF_PROBE(...) (__VA_ARGS__),
// Strip off the type
#define STRIP(x) EAT x
// Show the type without parenthesis
#define PAIR(x) REM x

接下来,我们定义一个 REFLECTABLE 宏来生成有关每个字段(以及字段本身)的数据。此宏将按如下方式调用:

REFLECTABLE
(
    (const char *) name,
    (int) age
)

因此,使用Boost.PP,我们迭代每个参数并生成如下数据: 

// A helper metafunction for adding const to a type
template<class M, class T>
struct make_const
{
    typedef T type;
};

template<class M, class T>
struct make_const<const M, T>
{
    typedef typename boost::add_const<T>::type type;
};


#define REFLECTABLE(...) \
static const int fields_n = BOOST_PP_VARIADIC_SIZE(__VA_ARGS__); \
friend struct reflector; \
template<int N, class Self> \
struct field_data {}; \
BOOST_PP_SEQ_FOR_EACH_I(REFLECT_EACH, data, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__))

#define REFLECT_EACH(r, data, i, x) \
PAIR(x); \
template<class Self> \
struct field_data<i, Self> \
{ \
    Self & self; \
    field_data(Self & self) : self(self) {} \
    \
    typename make_const<Self, TYPEOF(x)>::type & get() \
    { \
        return self.STRIP(x); \
    }\
    typename boost::add_const<TYPEOF(x)>::type & get() const \
    { \
        return self.STRIP(x); \
    }\
    const char * name() const \
    {\
        return BOOST_PP_STRINGIZE(STRIP(x)); \
    } \
}; \

这样做的作用是生成一个常量fields_n该常量是类中可反射字段的数量。然后,它专门针对每个字段field_data。它也与反射器类友好,这样即使它是私有的,它也可以访问这些字段:

struct reflector
{
    //Get field_data at index N
    template<int N, class T>
    static typename T::template field_data<N, T> get_field_data(T& x)
    {
        return typename T::template field_data<N, T>(x);
    }

    // Get the number of fields
    template<class T>
    struct fields
    {
        static const int n = T::fields_n;
    };
};

现在,为了迭代字段,我们使用访问者模式。我们创建一个从 0 到字段数的 MPL 范围,并访问该索引处的字段数据。然后,它将字段数据传递给用户提供的访问者:

struct field_visitor
{
    template<class C, class Visitor, class I>
    void operator()(C& c, Visitor v, I)
    {
        v(reflector::get_field_data<I::value>(c));
    }
};


template<class C, class Visitor>
void visit_each(C & c, Visitor v)
{
    typedef boost::mpl::range_c<int,0,reflector::fields<C>::n> range;
    boost::mpl::for_each<range>(boost::bind<void>(field_visitor(), boost::ref(c), v, _1));
}

 现在到了关键时刻,我们把它们放在一起。下面介绍了如何定义可反射的 Person 类:

struct Person
{
    Person(const char *name, int age)
        :
        name(name),
        age(age)
    {
    }
private:
    REFLECTABLE
    (
        (const char *) name,
        (int) age
    )
};

下面是一个广义print_fields函数,它使用反射数据来循环访问字段:

struct print_visitor
{
    template<class FieldData>
    void operator()(FieldData f)
    {
        std::cout << f.name() << "=" << f.get() << std::endl;
    }
};

template<class T>
void print_fields(T & x)
{
    visit_each(x, print_visitor());
}

将print_fields与可反射 Person 类结合使用的示例:

int main()
{
    Person p("Tom", 82);
    print_fields(p);
    return 0;
}

哪些输出:

name=Tom
age=82

 瞧,我们刚刚在C++中实现了反射,在不到100行的代码中。

 

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值