J'aimerais pouvoir introspecter une classe C ++ pour son nom, son contenu (c.-à-d. Les membres et leurs types), etc. Je parle ici du C ++ natif, pas du C ++ géré, qui a une réflexion. Je réalise que C ++ fournit des informations limitées en utilisant RTTI. Quelles autres bibliothèques (ou autres techniques) pourraient fournir cette information?
Réponses
Trop de publicités?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 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));
}
Voici maintenant le moment de vérité nous mettre tout cela ensemble. Voici comment on peut définir une Person
classe qui est reflectable:
struct Person
{
Person(const char *name, int age)
:
name(name),
age(age)
{
}
private:
REFLECTABLE
(
(const char *) name,
(int) age
)
};
Voici une généralisé print_fields
de la fonction en utilisant les données de réflexion pour itérer sur les champs:
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 d'utilisation de l' print_fields
avec le reflectable Person
classe:
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.
Il existe deux types d' reflection
autour de la piscine.
- L'Inspection par l'itération sur les membres d'un type, l'énumération de ses méthodes, et ainsi de suite.
Ce n'est pas possible en C++. - Inspection en vérifiant si une classe de type (class, struct, union) a une méthode ou d'un type imbriqué, est dérivé d'un autre type particulier.
Ce genre de chose est possible avec C++ à l'aide detemplate-tricks
. Utiliserboost::type_traits
pour beaucoup de choses (comme vérifier si un type fait partie intégrante). Pour la vérification de l'existence d'une fonction membre, utilisez http://stackoverflow.com/questions/257288/possible-for-c-template-to-check-for-a-functions-existence#264088 . Pour vérifier si un certain type imbriqué existe, utilisez la plaine SFINAE .
Si vous êtes plutôt à la recherche de moyens pour accomplir 1), comme le fait de regarder la façon dont de nombreuses méthodes d'une classe a, ou comme obtenir la représentation de chaîne d'un id de classe, alors j'ai peur qu'il n'existe pas de Norme C++ moyen de le faire. Vous devez utiliser
- Une Méta Compilateur comme l'intervalle Qt Compilateur de Méta-Objet qui se traduit par votre code d'ajouter des méta-informations.
- Un Cadre constisting de macros qui vous permettent d'ajouter la méta-informations. Vous devez dire le cadre de toutes les méthodes, les noms de classe de base des classes et tout ce qu'il faut.
C++ est faite avec de la vitesse à l'esprit. Si vous voulez de haut niveau de l'inspection, comme C# ou en Java, alors je crains que je dois vous dire, il n'est pas sans un certain effort.
Et j'aimerais un poney, mais les poneys ne sont pas libres. :-p
http://en.wikibooks.org/wiki/C%2B%2B_Programming/RTTI est ce que vous allez obtenir. La réflexion à laquelle vous pensez - les métadonnées entièrement descriptives disponibles au moment de l'exécution - n'existe pas pour C ++ par défaut.
Les informations existent, mais pas dans le format dont vous avez besoin, et uniquement si vous avez l'exportation de vos classes. Cela fonctionne sous Windows, je ne sais pas sur les autres plates-formes. À l'aide de la catégorie de stockage des prescripteurs, comme par exemple:
class __declspec(export) MyClass
{
public:
void Foo(float x);
}
Cela rend le compilateur construire la définition de la classe de données dans le DLL/Exe. Mais ce n'est pas dans un format que vous pouvez facilement utiliser pour la réflexion.
Dans mon entreprise, nous avons construit une bibliothèque qui interprète ces métadonnées, et permet de réfléchir à une classe sans insertion de macros supplémentaires etc. dans la classe elle-même. Il permet des fonctions d'être appelé comme suit:
MyClass *instance_ptr=new MyClass;
GetClass("MyClass")->GetFunction("Foo")->Invoke(instance_ptr,1.331);
Effectivement cela n':
instance_ptr->Foo(1.331);
Le Invoke(this_pointer,...) de la fonction de la variable d'arguments. De toute évidence, par l'appel d'une fonction dans cette façon, vous êtes en contournant les choses comme const-sécurité et ainsi de suite, de sorte que ces aspects sont mis en œuvre comme l'exécution des contrôles.
Je suis sûr que la syntaxe pourrait être améliorée, et il ne fonctionne que sur Win32 et Win64 jusqu'à présent. Nous avons trouvé vraiment utile pour avoir automatique d'interfaces GUI de classes, la création de propriétés en C++, en streaming et à partir de XML et ainsi de suite, et il n'y a pas besoin de dériver d'une classe de base. Si il y a assez de demande, peut-être qu'on pourrait la heurter en forme pour la libération.