Yang perlu Anda lakukan adalah memiliki preprocessor menghasilkan data refleksi tentang bidang. Data ini dapat disimpan sebagai kelas bersarang.
Pertama, untuk membuatnya lebih mudah dan lebih bersih untuk menulisnya di preprocessor kita akan menggunakan ekspresi yang diketik. Ekspresi yang diketik hanyalah ekspresi yang menempatkan jenis dalam tanda kurung. Jadi, alih-alih menulis, int x
Anda akan menulis (int) x
. Berikut adalah beberapa makro berguna untuk membantu dengan ekspresi yang diketik:
#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
Selanjutnya, kami mendefinisikan REFLECTABLE
makro untuk menghasilkan data tentang setiap bidang (ditambah bidang itu sendiri). Makro ini akan disebut seperti ini:
REFLECTABLE
(
(const char *) name,
(int) age
)
Jadi menggunakan Boost.PP kita beralih pada setiap argumen dan menghasilkan data seperti ini:
// 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)); \
} \
}; \
Apa yang dilakukan adalah menghasilkan konstanta fields_n
yang merupakan jumlah bidang yang dapat dipantulkan di kelas. Maka itu spesialisasi field_data
untuk setiap bidang. Ini juga teman reflector
kelas, ini sehingga dapat mengakses bidang bahkan ketika mereka pribadi:
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;
};
};
Sekarang untuk beralih ke bidang yang kita gunakan pola pengunjung. Kami membuat rentang MPL dari 0 hingga jumlah bidang, dan mengakses data bidang pada indeks itu. Kemudian data lapangan diteruskan ke pengunjung yang disediakan pengguna:
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));
}
Sekarang untuk momen kebenaran, kami menggabungkan semuanya. Inilah cara kita mendefinisikan Person
kelas yang dapat dipantulkan:
struct Person
{
Person(const char *name, int age)
:
name(name),
age(age)
{
}
private:
REFLECTABLE
(
(const char *) name,
(int) age
)
};
Berikut adalah print_fields
fungsi umum yang menggunakan data refleksi untuk beralih di bidang:
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());
}
Contoh menggunakan print_fields
dengan Person
kelas yang dapat dipantulkan :
int main()
{
Person p("Tom", 82);
print_fields(p);
return 0;
}
Output yang mana:
name=Tom
age=82
Dan voila, kami baru saja mengimplementasikan refleksi dalam C ++, di bawah 100 baris kode.