Ce que vous devez faire est d'avoir le préprocesseur générer des données de réflexion sur le champs. Ces données peuvent être stockées en tant que classes imbriquées.
Tout d'abord, pour le rendre plus facile et plus propre de l'écrire dans le préprocesseur, nous allons utiliser tapé expression. Typé expression est juste une expression qui met le type entre parenthèses. Donc au lieu d'écrire int x
vous écrirez (int) x
. Voici quelques macros rapides pour aider à tapé 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
Ensuite, nous définissons un REFLECTABLE
macro pour générer les données relatives à chaque domaine(ainsi que le champ lui-même). Cette macro sera appelée comme ceci:
REFLECTABLE
(
(const char *) name,
(int) age
)
Donc, à l'aide de coup de pouce.PP nous itérer sur chaque argument et de générer les données comme ceci:
// 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)); \
} \
}; \
Ce qu'il fait est de générer une constante fields_n
c'est le nombre de reflectable champs de la classe. Puis il se spécialise l' field_data
pour chaque champ. Il a aussi des amis de l' reflector
classe, c'est de sorte qu'il peut accéder aux champs, même quand ils sont privés:
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;
};
};
Maintenant pour itérer sur les champs, nous utilisons le modèle visiteur. Nous avons créer une MPL comprise entre 0 et le nombre de champs, et d'accéder aux données de terrain à l'index. Elle transmet ensuite les données de terrain sur l'utilisateur fourni par visiteur:
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));
}
Voici maintenant le moment de vérité nous mettre tout cela ensemble. Voici comment nous pouvons définir l' Person
classe:
struct Person
{
Person(const char *name, int age)
:
name(name),
age(age)
{
}
private:
REFLECTABLE
(
(const char *) name,
(int) age
)
};
Ici est la généralisation de l' print_fields
fonction de:
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());
}
Un exemple:
int main()
{
Person p("Tom", 82);
print_fields(p);
return 0;
}
Sorties:
name=Tom
age=82
Et voila, nous venons de mise en œuvre de réflexion en C++, en moins de 100 lignes de code.