Was Sie tun müssen, ist, dass der Präprozessor Reflexionsdaten über die Felder generiert. Diese Daten können als verschachtelte Klassen gespeichert werden.
Um es einfacher und sauberer zu machen, es in den Präprozessor zu schreiben, verwenden wir zunächst einen typisierten Ausdruck. Ein typisierter Ausdruck ist nur ein Ausdruck, der den Typ in Klammern setzt. Also anstatt zu schreiben, int x
wirst du schreiben (int) x
. Hier sind einige nützliche Makros, die bei getippten Ausdrücken helfen:
#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
Als nächstes definieren wir ein REFLECTABLE
Makro, um die Daten für jedes Feld (plus das Feld selbst) zu generieren. Dieses Makro wird folgendermaßen aufgerufen:
REFLECTABLE
(
(const char *) name,
(int) age
)
Mit Boost.PP iterieren wir also über jedes Argument und generieren die Daten wie folgt :
// 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)); \
} \
}; \
Dadurch wird eine Konstante generiert fields_n
, die die Anzahl der reflektierbaren Felder in der Klasse darstellt. Dann ist es das field_data
für jedes Feld spezialisiert. Es befreundet auch die reflector
Klasse, so dass es auf die Felder zugreifen kann, auch wenn sie privat sind:
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;
};
};
Um nun über die Felder zu iterieren, verwenden wir das Besuchermuster. Wir erstellen einen MPL-Bereich von 0 bis zur Anzahl der Felder und greifen auf die Felddaten an diesem Index zu. Anschließend werden die Felddaten an den vom Benutzer bereitgestellten Besucher weitergeleitet:
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));
}
Jetzt für den Moment der Wahrheit setzen wir alles zusammen. So können wir eine Person
Klasse definieren , die reflektierbar ist:
struct Person
{
Person(const char *name, int age)
:
name(name),
age(age)
{
}
private:
REFLECTABLE
(
(const char *) name,
(int) age
)
};
Hier ist eine verallgemeinerte print_fields
Funktion, die die Reflexionsdaten verwendet, um über die Felder zu iterieren:
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());
}
Ein Beispiel für die Verwendung der print_fields
mit der reflektierbaren Person
Klasse:
int main()
{
Person p("Tom", 82);
print_fields(p);
return 0;
}
Welche Ausgänge:
name=Tom
age=82
Und voila, wir haben gerade die Reflexion in C ++ in weniger als 100 Codezeilen implementiert.