What you need to do is that the preprocessor generates the reflection data in the fields. This data can be stored as nested classes.
First, to simplify and clean it in the preprocessor, we will use a typed expression. A typed expression is simply an expression that puts the type in parentheses. So instead of writing int x you will write (int) x . Here are some useful macros to help with typed expressions:
#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
Next, we define the REFLECTABLE macro to generate data about each field (plus the field itself). This macro will be called like this:
REFLECTABLE ( (const char *) name, (int) age )
Thus, using Boost.PP , we iterate over each argument and generate such data:
// 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)); \ } \ }; \
What it is, generates the fields_n constant, which is the number of reflected fields in the class. He then specializes in field_data for each field. It also supports the reflector class, so it can access fields even if they are private:
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; }; };
Now, to iterate over the fields, we use the visitor template. We create an MPL range from 0 to the number of fields and get access to the field data at this index. Then it passes the field data to the visitor to whom the user is provided:
struct field_visitor { template<class C, class Visitor, class T> void operator()(C& c, Visitor v, T) { v(reflector::get_field_data<T::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)); }
Now, for a moment of truth, we are all together. Here is how we can define the Person class:
struct Person { Person(const char *name, int age) : name(name), age(age) { } private: REFLECTABLE ( (const char *) name, (int) age ) };
Here is the generic print_fields function:
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()); }
Example:
int main() { Person p("Tom", 82); print_fields(p); return 0; }
What outputs:
name=Tom age=82
And voila, we just implemented reflection in C ++, within up to 100 lines of code.