สิ่งที่คุณต้องทำคือให้ตัวประมวลผลล่วงหน้าสร้างข้อมูลการสะท้อนเกี่ยวกับฟิลด์ ข้อมูลนี้สามารถจัดเก็บเป็นคลาสที่ซ้อนกัน
ก่อนอื่นเพื่อให้ง่ายและสะอาดยิ่งขึ้นในการเขียนลงใน preprocessor เราจะใช้นิพจน์ที่พิมพ์ การแสดงออกที่พิมพ์เป็นเพียงการแสดงออกที่ทำให้ประเภทในวงเล็บ ดังนั้นแทนที่จะเขียนคุณจะเขียน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
แต่ละฟิลด์ นอกจากนี้ยังเป็นเพื่อนกับreflector
ชั้นเรียนด้วยดังนั้นจึงสามารถเข้าถึงเขตข้อมูลได้แม้ว่าพวกเขาจะเป็นส่วนตัว:
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;
};
};
ทีนี้เพื่อย้ำเหนือช่องที่เราใช้รูปแบบผู้เยี่ยมชม เราสร้างช่วง MPL ตั้งแต่ 0 ถึงจำนวนของฟิลด์และเข้าถึงข้อมูลฟิลด์ที่ดัชนีนั้น จากนั้นจะส่งข้อมูลฟิลด์ไปยังผู้เข้าชมที่ผู้ใช้ระบุ:
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
และ voila เราเพิ่งนำการสะท้อนกลับมาใช้ในภาษาซีพลัสพลัสในโค้ดต่ำกว่า 100 บรรทัด