Yapmanız gereken, önişlemcinin alanlar hakkında yansıma verileri üretmesidir. Bu veriler iç içe sınıflar olarak saklanabilir.
İlk olarak, önişlemciye yazmayı daha kolay ve daha temiz hale getirmek için yazılı ifadeyi kullanacağız. Yazılan ifade yalnızca türü parantez içine alan bir ifadedir. Yani int x
yazmak yerine yazacaksınız (int) x
. Yazılan ifadelere yardımcı olacak bazı kullanışlı makrolar:
#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
Daha sonra, REFLECTABLE
her alanla (artı alanın kendisi) ilgili verileri üretmek için bir makro tanımlarız . Bu makro şu şekilde adlandırılacaktır:
REFLECTABLE
(
(const char *) name,
(int) age
)
Boost.PP'yi kullanarak her bir argümanın üzerinde tekrarlıyoruz ve bu şekilde veri üretiyoruz:
// 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)); \
} \
}; \
Bunun yaptığı, fields_n
sınıftaki yansıtılabilir alanların sayısı olan bir sabit oluşturmaktır . Sonra field_data
her alan için uzmanlaşmıştır . Ayrıca reflector
sınıfı arkadaş , bu da özel olsa bile alanlara erişebilmesi için:
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;
};
};
Şimdi alanları yinelemek için ziyaretçi modelini kullanıyoruz. 0 ile alan sayısı arasında bir MPL aralığı oluştururuz ve bu dizindeki alan verilerine erişiriz. Ardından alan verilerini kullanıcı tarafından sağlanan ziyaretçiye aktarır:
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));
}
Şimdi gerçek an için hepsini bir araya getirdik. Yansıtılabilir bir Person
sınıfı şu şekilde tanımlayabiliriz :
struct Person
{
Person(const char *name, int age)
:
name(name),
age(age)
{
}
private:
REFLECTABLE
(
(const char *) name,
(int) age
)
};
print_fields
Alanlar üzerinde yineleme yapmak için yansıma verilerini kullanan genelleştirilmiş bir işlev şunlardır:
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
Yansıtılabilir Person
sınıfla birlikte kullanımına bir örnek :
int main()
{
Person p("Tom", 82);
print_fields(p);
return 0;
}
Hangi çıktılar:
name=Tom
age=82
Ve işte, C ++ 'da 100 kodun altında yansıma uyguladık.