Những gì bạn cần làm là để bộ tiền xử lý tạo dữ liệu phản ánh về các trường. Dữ liệu này có thể được lưu trữ dưới dạng các lớp lồng nhau.
Đầu tiên, để làm cho nó dễ dàng và sạch sẽ hơn để viết nó trong bộ tiền xử lý, chúng ta sẽ sử dụng biểu thức gõ. Một biểu thức gõ chỉ là một biểu thức đặt kiểu trong ngoặc đơn. Vì vậy, thay vì viết int x
bạn sẽ viết (int) x
. Dưới đây là một số macro tiện dụng để trợ giúp với các biểu thức được nhập:
#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
Tiếp theo, chúng tôi xác định một REFLECTABLE
macro để tạo dữ liệu về từng trường (cộng với chính trường đó). Macro này sẽ được gọi như thế này:
REFLECTABLE
(
(const char *) name,
(int) age
)
Vì vậy, bằng cách sử dụng Boost.PP, chúng tôi lặp lại qua từng đối số và tạo dữ liệu như thế này:
// 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)); \
} \
}; \
Điều này không tạo ra một hằng số fields_n
là số trường có thể phản xạ trong lớp. Sau đó, nó chuyên field_data
cho từng lĩnh vực. Nó cũng kết bạn với reflector
lớp, điều này là để nó có thể truy cập vào các trường ngay cả khi chúng ở chế độ riêng tư:
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;
};
};
Bây giờ để lặp lại qua các trường, chúng tôi sử dụng mẫu khách truy cập. Chúng tôi tạo phạm vi MPL từ 0 đến số lượng trường và truy cập dữ liệu trường tại chỉ mục đó. Sau đó, nó chuyển dữ liệu trường cho khách truy cập do người dùng cung cấp:
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));
}
Bây giờ cho khoảnh khắc của sự thật, chúng tôi đặt tất cả lại với nhau. Đây là cách chúng ta có thể định nghĩa một Person
lớp có thể phản ánh:
struct Person
{
Person(const char *name, int age)
:
name(name),
age(age)
{
}
private:
REFLECTABLE
(
(const char *) name,
(int) age
)
};
Đây là một print_fields
hàm tổng quát sử dụng dữ liệu phản chiếu để lặp lại qua các trường:
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());
}
Một ví dụ về việc sử dụng print_fields
với Person
lớp có thể phản chiếu :
int main()
{
Person p("Tom", 82);
print_fields(p);
return 0;
}
Đầu ra nào:
name=Tom
age=82
Và voila, chúng tôi vừa thực hiện phản chiếu trong C ++, với hơn 100 dòng mã.