Comment puis-je ajouter une réflexion à une application c++?

J'aimerais pouvoir introspecter une Classe C++ pour son nom, son contenu (c'est-à-dire les membres et leurs types), etc. Je parle de C++ natif ici, pas de C++ géré, qui a une réflexion. Je me rends compte que C++ fournit des informations limitées en utilisant RTTI. Quelles bibliothèques supplémentaires (ou d'autres techniques) pourraient fournir cette information?

213
demandé sur Lazer 2008-09-03 14:59:05

30 réponses

Ce que vous devez faire est que le préprocesseur génère des données de réflexion sur les 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 à écrire dans le préprocesseur, nous allons utiliser l'expression typée. Typé expression est juste une expression qui met le type entre parenthèses. Donc au lieu d'écrire int x, vous écrivez (int) x. Voici quelques macros pratiques pour aider avec les expressions typées:

#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 une macro REFLECTABLE pour générer les données sur chaque champ (plus le champ lui-même). Cette macro sera appelée comme ceci:

REFLECTABLE
(
    (const char *) name,
    (int) age
)

Donc en utilisant Boost.PP nous itérons sur chaque argument et générons 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 que cela fait est de générer une constante fields_n qui est le nombre de champs réflectables dans la classe. Ensuite, il se spécialise le field_data pour chaque champ. Il ami aussi la classe reflector, c'est ainsi qu'il peut accéder aux champs même lorsqu'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 à itérer sur les champs que nous utilisons le modèle de visiteur. Nous créons une plage MPL allant de 0 au nombre de champs, et accédons aux données de champ à cet index. Ensuite, il transmet les données de champ au visiteur fourni par l'utilisateur:

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));
}

Maintenant, pour le moment de vérité, nous mettons tout cela ensemble. Voici comment nous pouvons définir une classe Person qui peut être réfléchie:

struct Person
{
    Person(const char *name, int age)
        :
        name(name),
        age(age)
    {
    }
private:
    REFLECTABLE
    (
        (const char *) name,
        (int) age
    )
};

Voici une fonction print_fields généralisée 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 le print_fields avec la classe Person réflectable:

int main()
{
    Person p("Tom", 82);
    print_fields(p);
    return 0;
}

Quelles sorties:

name=Tom
age=82

Et voilà, nous venons d'implémenter la réflexion en C++, en moins de 100 lignes de code.

203
répondu Paul Fultz II 2013-05-20 16:45:45

Il y a deux sortes de reflection nageant autour.

  1. Inspection en itérant sur les membres d'un type, en énumérant ses méthodes et ainsi de suite.

    Ce n'est pas possible en C++.
  2. Inspection en vérifiant si un type de classe (class, struct, union) a une méthode ou un type imbriqué, est dérivé d'un autre type particulier.

    Ce genre de chose est possible avec C++ en utilisant template-tricks. Utilisation boost::type_traits pour beaucoup de choses (comme vérifier si un type fait partie intégrante). Pour vérification de l'existence d'une fonction membre, utilisez Est-il possible d'écrire un modèle pour vérifier la fonction de l'existence? . Pour vérifier si un certain type imbriqué existe, utilisez plain SFINAE .

Si vous cherchez plutôt des moyens d'accomplir 1), comme regarder combien de méthodes une classe A, ou comme obtenir la représentation de chaîne d'un id de classe, alors j'ai peur qu'il n'y ait pas de façon Standard C++ de le faire. Vous devez utiliser soit

  • Un Meta compilateur comme le compilateur Qt Meta Object qui traduit votre code en ajoutant des informations méta supplémentaires.
  • un Framework constitué de macros permettant d'ajouter les méta-informations requises. Vous devez indiquer au framework toutes les méthodes, les noms de classe, les classes de base et tout ce dont il a besoin.

C++ est fait avec la vitesse à l'esprit. Si vous voulez une inspection de haut niveau, comme C# ou Java, alors je crains de devoir vous dire qu'il n'y a aucun moyen sans effort.

96
répondu Johannes Schaub - litb 2017-05-23 12:34:45

Et j'adorerais 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 comme vous pensez à-métadonnées entièrement descriptives disponibles à l'exécution-n'existe tout simplement pas pour C++ par défaut.

55
répondu Brad Wilson 2008-09-03 11:04:56

L'information existe - mais pas dans le format dont vous avez besoin, et seulement si vous exportez vos classes. Cela fonctionne dans Windows, Je ne connais pas d'autres plates-formes. Utilisation des spécificateurs de classe de stockage comme dans, par exemple:

class __declspec(export) MyClass
{
public:
    void Foo(float x);
}

Cela permet au compilateur de construire les données de définition de classe dans la 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 vous permet de refléter une classe sans insertion de macros supplémentaires, etc. dans la classe elle-même. Il permet aux fonctions d'être appelées comme suit:

MyClass *instance_ptr=new MyClass;
GetClass("MyClass")->GetFunction("Foo")->Invoke(instance_ptr,1.331);

Cela fait effectivement:

instance_ptr->Foo(1.331);

L'Invoke (this_pointer,...) la fonction a des arguments variables. Évidemment, en appelant une fonction de cette façon, vous contournez des choses comme const-safety et ainsi de suite, de sorte que ces aspects sont implémentés en tant que vérifications d'exécution.

Je suis sûr que la syntaxe pourrait être améliorée, et cela ne fonctionne que sur Win32 et Win64 jusqu'à présent. Nous avons trouvé vraiment utile pour avoir des interfaces graphiques automatiques pour les classes, créer des propriétés en C++, diffuser vers et depuis XML, etc., et il n'est pas nécessaire de dériver d'une classe de base spécifique. S'il y a assez de demande, peut-être que nous pourrions le mettre en forme pour la libération.

36
répondu Roderick 2011-08-11 23:06:26

RTTI n'existe pas pour C++.

C'est tout simplement faux. En fait, le terme même "RTTI" a été inventé par la norme c++. D'autre part, RTTI ne va pas très loin dans la mise en œuvre de la réflexion.

35
répondu Konrad Rudolph 2008-09-03 13:18:29

Vous devez regarder ce que vous essayez de faire, et si RTTI répondra à vos besoins. J'ai implémenté ma propre pseudo-réflexion à des fins très spécifiques. Par exemple, je voulais être en mesure de configurer de manière flexible ce qu'une simulation produirait. Il fallait ajouter du code standard aux classes qui seraient sorties:

namespace {
  static bool b2 = Filter::Filterable<const MyObj>::Register("MyObject");
} 

bool MyObj::BuildMap()
{
  Filterable<const OutputDisease>::AddAccess("time", &MyObj::time);
  Filterable<const OutputDisease>::AddAccess("person", &MyObj::id);
  return true;
}

Le premier appel ajoute cet objet au système de filtrage, qui appelle la méthode BuildMap() pour déterminer quelles méthodes sont disponibles.

, Puis, dans le fichier de configuration, vous pouvez faire quelque chose comme ceci:

FILTER-OUTPUT-OBJECT   MyObject
FILTER-OUTPUT-FILENAME file.txt
FILTER-CLAUSE-1        person == 1773
FILTER-CLAUSE-2        time > 2000

Grâce à une magie de modèle impliquant boost, cela se traduit par une série d'appels de méthode au moment de l'exécution (lorsque le fichier de configuration est lu), donc c'est assez efficace. Je ne recommanderais pas de faire cela sauf si vous en avez vraiment besoin, mais, quand vous le faites, vous pouvez faire des choses vraiment cool.

14
répondu KeithB 2014-08-23 16:20:51

Qu'essayez-vous de faire avec la réflexion?
Vous pouvez utiliser les bibliothèques Boost Type traits et typeof comme une forme limitée de réflexion à la compilation. Autrement dit, Vous pouvez inspecter et modifier les propriétés de base d'un type transmis à un modèle.

13
répondu Ferruccio 2008-09-03 11:33:05

Je recommande d'utiliser Qt .

Il existe une licence open-source ainsi qu'une licence commerciale.

13
répondu Jérôme 2008-09-03 13:06:38

Modifier: le CAMP n'est plus entretenu; deux fourches sont disponibles:

  • On est aussi appelé CAMP , et est basé sur la même API.
  • Ponder est une réécriture partielle, et doit être préférée car elle ne nécessite pas de Boost; elle utilise C++11.

CAMP {[4] } est une bibliothèque sous licence MIT (anciennement LGPL) qui ajoute une réflexion au langage C++. Il ne nécessite pas d'étape de prétraitement spécifique dans la compilation, mais la liaison doit être faite manuellement.

La bibliothèque actuelle de Tegesoft utilise Boost, mais il y a aussi un fork utilisant C++11 Qui ne nécessite plus Boost.

11
répondu philant 2017-10-10 12:50:53

J'ai fait quelque chose comme ce que vous recherchez une fois, et bien qu'il soit possible d'obtenir un certain niveau de réflexion et d'accéder à des fonctionnalités de niveau supérieur, le mal de tête de maintenance pourrait ne pas en valoir la peine. Mon système a été utilisé pour garder les classes D'interface utilisateur complètement séparées de la logique métier grâce à la délégation semblable au concept D'Objective-C de transmission et de transfert de messages. La façon de le faire est de créer une classe de base capable de mapper des symboles (j'ai utilisé un pool de chaînes mais vous pouvez le faire avec enums si vous préférez la vitesse et la gestion des erreurs à la compilation sur la flexibilité totale) aux pointeurs de fonction (en fait pas des pointeurs de fonction purs, mais quelque chose de similaire à ce que Boost A avec Boost.Fonction-à laquelle je n'avais pas accès à l'époque). Vous pouvez faire la même chose pour vos variables membres tant que vous avez une classe de base commune capable de représenter n'importe quelle valeur. L'ensemble du système était une arnaque sans faille de codage clé-valeur et de délégation, avec quelques effets secondaires qui étaient peut-être cela vaut le temps nécessaire pour que chaque classe qui utilisait le système corresponde à toutes ses méthodes et membres avec des appels légaux: 1) n'importe quelle classe pourrait appeler n'importe quelle méthode sur n'importe quelle autre classe sans avoir à inclure des en-têtes ou écrire de fausses classes de base afin que l'interface puisse être prédéfinie pour le compilateur; et 2) tous les objets.

Cela a également conduit à la possibilité de faire des choses vraiment bizarres qui ne sont pas faciles en C++. Par exemple, je pourrais créer un objet tableau contenant des éléments arbitraires de tout type, y compris lui-même, et créer de nouveaux tableaux dynamiquement en passant un message à tous les éléments du tableau et en collectant les valeurs de retour (similaire à map en Lisp). Une autre était la mise en œuvre de l'observation de la valeur clé, par laquelle j'ai été en mesure de mettre en place l'interface utilisateur pour répondre immédiatement aux changements dans le membres des classes backend au lieu d'interroger constamment les données ou de redessiner inutilement l'affichage.

Peut-être plus intéressant pour vous est le fait que vous pouvez également vider toutes les méthodes et les membres définis pour une classe, et sous forme de chaîne pas moins.

Inconvénients du système qui pourraient vous décourager de vous embêter: ajouter tous les messages et les valeurs clés est extrêmement fastidieux; c'est plus lent que sans aucune réflexion; vous allez détester voir boost::static_pointer_cast et boost::dynamic_pointer_cast partout dans votre codebase avec une passion violente; les limites du système fortement typé sont toujours là, vous les cachez vraiment un peu, donc ce n'est pas aussi évident. Les fautes de frappe dans vos chaînes ne sont pas non plus une surprise amusante ou facile à découvrir.

Quant à la façon d'implémenter quelque chose comme ceci: il suffit d'utiliser des pointeurs partagés et faibles vers une base commune (le mien était très imaginativement appelé "objet") et dériver pour tous les types que vous voulez utiliser. Je recommande d'installer Boost.Fonction au lieu de le faire de la manière Je l'ai fait, ce qui était avec de la merde personnalisée et une tonne de macros laides pour envelopper les appels de pointeur de fonction. Puisque tout est mappé, l'inspection des objets est juste une question d'itérer à travers toutes les clés. Puisque mes classes étaient essentiellement aussi proches d'une arnaque directe de Cocoa que possible en utilisant uniquement C++, si vous voulez quelque chose comme ça, je suggérerais d'utiliser la documentation Cocoa comme plan.

10
répondu Michel 2008-11-27 21:41:28

Les deux solutions de type réflexion que je connais de mes jours c++ sont:

1) Utilisez RTTI, qui vous fournira un bootstrap pour construire votre comportement de type réflexion, si vous êtes capable de faire dériver toutes vos classes à partir d'une classe de base 'object'. Cette classe pourrait fournir des méthodes comme GetMethod, GetBaseClass etc. En ce qui concerne le fonctionnement de ces méthodes, vous devrez ajouter manuellement des macros pour décorer vos types, qui, dans les coulisses, créent des métadonnées dans le type pour fournir des réponses GetMethods etc.

2) une autre option, si vous avez accès aux objets du compilateur est d'utiliser le SDK DIA . Si je me souviens bien, cela vous permet d'ouvrir des PDB, qui devraient contenir des métadonnées pour vos types C++. Il pourrait être suffisant pour faire ce dont vous avez besoin. Cette page montre comment vous pouvez obtenir tous les types de base d'une classe par exemple.

Ces deux solutions sont un peu laides! Il n'y a rien comme un peu de C++ pour vous faire apprécier le luxe de C#.

Bon Chance.

9
répondu user4385 2008-09-03 12:13:09

Il existe une autre nouvelle bibliothèque de réflexion en C++, appelée RTTR (Type D'exécution réflexion, voir aussi github).

L'interface est similaire à la réflexion en C# et fonctionne sans RTTI.

7
répondu Zack 2016-01-15 10:22:51

EDIT: mise à jour du lien brisé à partir du 7 février 2017.

Je pense que personne n'a mentionné ceci:

Au CERN, ils utilisent un système de réflexion complet pour C++:

Le CERN Réflexe. Il semble très bien fonctionner.

7
répondu Germán Diago 2017-02-07 04:48:08

Cette question est un peu ancienne maintenant (je ne sais pas pourquoi je continue à poser de vieilles questions aujourd'hui) mais je pensais à BOOST_FUSION_ADAPT_STRUCT qui introduit la réflexion à la compilation.

C'est à vous de mapper ceci à la réflexion d'exécution bien sûr, et ce ne sera pas trop facile, mais c'est possible dans cette direction, alors que ce ne serait pas dans le sens inverse:)

Je pense vraiment qu'une macro pour encapsuler les BOOST_FUSION_ADAPT_STRUCT on pouvait générer les méthodes nécessaires pour obtenir l'exécution comportement.

6
répondu Matthieu M. 2010-11-12 17:05:53

Je pense que vous pourriez trouver intéressant L'article "utilisation de modèles pour la réflexion en C++" par Dominic Filion. Il est dans la section 1.4 de jeu de programmation Gems 5 . Malheureusement je n'ai pas mon exemplaire avec moi, mais parce que je pense qu'il explique ce que vous demandez.

5
répondu Luis 2017-11-03 15:15:45

La réflexion n'est pas prise en charge par C++. C'est triste parce que cela rend les tests défensifs une douleur.

Il existe plusieurs approches pour faire de la réflexion:

  1. Utilisez les informations de débogage (non portable).
  2. saupoudrez votre code avec des macros/templates ou une autre approche source (semble moche)
  3. modifiez un compilateur tel que clang / gcc pour produire une base de données.
  4. utiliser l'approche Qt moc
  5. Boost Reflète
  6. Précis et Plat La réflexion

Le premier lien semble le plus prometteur (utilise des mod pour clang), le second discute d'un certain nombre de techniques, le troisième est une approche différente en utilisant gcc:

  1. Http://www.donw.org/rfl/

  2. Https://bitbucket.org/dwilliamson/clreflect

  3. Https://root.cern.ch/how/how-use-reflex

Il y a maintenant un groupe de travail pour la réflexion C++. Voir les nouvelles pour C++14 @ CERN:

Édition 13/08/17: Depuis le poste original, il y a eu un certain nombre de progrès potentiels sur la réflexion. Ce qui suit fournit plus de détails et une discussion sur les différentes techniques et le statut:

  1. réflexion statique en un mot
  2. Statique De La Réflexion
  3. Une conception pour la réflexion statique

Cependant, il ne semble pas prometteur sur une approche de réflexion standardisée en C++ dans un proche avenir à moins que la communauté ne s'intéresse beaucoup plus à la réflexion en C++.

Ce qui suit détaille l'état actuel en fonction des commentaires de la dernière réunion des normes C++:

Modifier 13/12/2017

La réflexion semble se diriger vers C++ 20 ou plus probablement un TSR. Le mouvement est cependant lent.

Modifier 15/09/2018

Un projet de TS a été envoyé aux instances nationales pour vote.

, Le texte peut être trouvé ici: https://github.com/cplusplus/reflection-ts

5
répondu Damian Dixon 2018-09-15 12:15:05

Ponder est une bibliothèque de réflexion C++, en réponse à cette question. J'ai considéré les options et j'ai décidé de faire la mienne car je ne pouvais pas en trouver une qui cochait toutes mes cases.

Bien qu'il y ait d'excellentes réponses à cette question, Je ne veux pas utiliser des tonnes de macros, ou compter sur Boost. Boost est une grande bibliothèque, mais il y a beaucoup de petits projets C++0x sur mesure qui sont plus simples et ont des temps de compilation plus rapides. Il y a aussi des avantages à pouvoir décorer une classe extérieurement, comme envelopper une bibliothèque C++ qui ne le fait pas (encore?) soutien C++11. C'est fork de CAMP, en utilisant C++11, que ne nécessite plus de Boost.

4
répondu Nick 2016-05-17 09:05:48

La réflexion concerne essentiellement ce que le compilateur a décidé de laisser comme empreintes dans le code que le code d'exécution peut interroger. C++ est célèbre pour ne pas payer pour ce que vous n'utilisez pas; parce que la plupart des gens n'utilisent pas / veulent la réflexion, le compilateur C++ évite le coût en n'enregistrant rien .

Donc, C++ ne fournit pas de réflexion, et il n'est pas facile de le "simuler" vous-même en règle générale comme d'autres réponses l'ont noté.

Sous "autres techniques", si vous n'avez pas langage avec réflexion, Obtenez un outil qui peut extraire les informations que vous voulez au moment de la compilation.

Notre DMS Software Reengineering Toolkit est une technologie de compilateur généralisée paramétrée par des définitions langauge explicites. Il a des définitions langauge pour C, C++, Java, COBOL, PHP,...

Pour les versions C, C++, Java et COBOL, il fournit un accès complet aux arbres d'analyse et aux informations de table de symboles. Cette information de table de symbole inclut le genre de les données que vous êtes susceptible de vouloir de "réflexion". Si votre objectif est d'énumérer un ensemble de champs ou de méthodes et Faire quelque chose avec eux, DMS peut être utilisé pour transformer le code en fonction de ce que vous trouvez dans les tables de symboles de manière arbitraire.

4
répondu Ira Baxter 2016-11-08 00:00:18

Vous pouvez trouver une autre bibliothèque ici: http://www.garret.ru/cppreflection/docs/reflect.html Il prend en charge 2 façons: obtenir des informations de type à partir des informations de débogage et laisser programmeur pour fournir ces informations.

Je me suis aussi intéressé à la réflexion pour mon projet et j'ai trouvé cette bibliothèque, Je ne l'ai pas encore essayé, mais j'ai essayé d'autres outils de ce gars et j'aime comment ils fonctionnent: -)

3
répondu alariq 2011-07-28 08:45:18

Consulter Classdesc http://classdesc.sf.net. Il fournit une réflexion sous la forme de "descripteurs" de classe, fonctionne avec n'importe quel compilateur C++ standard (Oui, il est connu pour fonctionner avec Visual Studio ainsi que GCC), et ne nécessite pas d'annotation de code source (bien que certains pragmas existent pour gérer des situations délicates). Il a été en développement pendant plus d'une décennie, et utilisé dans un certain nombre de projets à l'échelle industrielle.

3
répondu Russell Standish 2013-04-06 07:41:51

Quand je voulais une réflexion en C++ j'ai Lu Cet article et amélioré ce que j'ai vu là-bas. Désolé, non est. Je n'ai pas le résultat...mais vous pouvez certainement obtenir ce que j'avais et partir de là.

Je recherche actuellement, Quand j'en ai envie, des méthodes pour utiliser inherit_linearly pour faciliter la définition des types réflectables. Je suis allé assez loin en fait, mais j'ai encore du chemin à parcourir. Les changements dans C++0x sont très susceptibles d'avoir beaucoup d'aide dans ce zone.

2
répondu Crazy Eddie 2010-06-14 04:12:23

Il semble que C++ n'ait toujours pas cette fonctionnalité. Et C++11 reporté aussi de la réflexion ((

Rechercher des macros ou faire propre. Qt peut également aider à la réflexion (si elle peut être utilisée).

2
répondu Bohdan 2011-11-24 08:23:11

Essayer de regarder ce projet http://www.garret.ru/cppreflection/docs/reflect.html est ajouté réflexions à C++. Il a ajouté des méta-données aux classes que vous pouvez ensuite utiliser.

2
répondu Damian 2012-02-17 10:03:19

Même si la réflexion n'est pas prise en charge en c++, elle n'est pas trop difficile à implémenter. J'ai rencontré ce grand article: http://replicaisland.blogspot.co.il/2010/11/building-reflective-object-system-in-c.html

L'article explique en détail comment vous pouvez implémenter un système de réflexion assez simple et rudimentaire. certes, ce n'est pas la solution la plus saine, et il reste des bords rugueux à régler, mais pour mes besoins, c'était suffisant.

La réflexion de la ligne de fond peut être payante si elle est effectuée correctement, et c'est complètement faisable en C++.

2
répondu Naore Azenkut 2013-02-02 23:19:14

Je voudrais annoncer l'existence de la boîte à outils d'introspection/réflexion automatique "IDK". Il utilise un méta-compilateur comme Qt et ajoute des méta-informations directement dans les fichiers objet. Il est prétendu être facile à utiliser. Pas de dépendances externes. Il vous permet même de refléter automatiquement std:: string, puis de l'utiliser dans des scripts. Veuillez regarder IDK

2
répondu Jen 2014-03-12 12:49:44

La réflexion en c++ est très utile, dans les cas où vous devez exécuter une méthode pour chaque membre (par exemple: sérialisation, hachage, comparaison). Je suis venu avec une solution générique, avec une syntaxe très simple:

struct S1
{
    ENUMERATE_MEMBERS(str,i);
    std::string str;
    int i;
};
struct S2
{
    ENUMERATE_MEMBERS(s1,i2);
    S1 s1;
    int i2;
};

Où ENUMERATE_MEMBERS est une macro, qui est décrite plus loin (mise à jour):

Supposons que nous ayons défini la fonction de sérialisation pour int et std:: string comme ceci:

void EnumerateWith(BinaryWriter & writer, int val)
{
    //store integer
    writer.WriteBuffer(&val, sizeof(int));
}
void EnumerateWith(BinaryWriter & writer, std::string val)
{
    //store string
    writer.WriteBuffer(val.c_str(), val.size());
}

Et nous avons une fonction générique près de la "macro secrète";)

template<typename TWriter, typename T>
auto EnumerateWith(TWriter && writer, T && val) -> is_enumerable_t<T>
{
    val.EnumerateWith(write); //method generated by ENUMERATE_MEMBERS macro
}

Maintenant vous pouvez écrire

S1 s1;
S2 s2;
//....
BinaryWriter writer("serialized.bin");

EnumerateWith(writer, s1); //this will call EnumerateWith for all members of S1
EnumerateWith(writer, s2); //this will call EnumerateWith for all members of S2 and S2::s1 (recursively)

Donc, ayant la macro ENUMERATE_MEMBERS dans la définition de la structure, vous pouvez construire la sérialisation, la comparaison, le hachage et d'autres éléments sans toucher au type d'origine, la seule exigence est d'implémenter la méthode "EnumerateWith" pour chaque type, qui n'est pas énumérable, par énumérateur (comme BinaryWriter). Habituellement, vous devrez implémenter 10-20 types "simples" pour prendre en charge n'importe quel type dans votre projet.

Cette macro devrait avoir une surcharge nulle pour la création/destruction de la structure au moment de l'exécution, et le code de T. EnumerateWith () doit être généré à la demande, ce qui peut être réalisé en le rendant fonction template-inline, donc la seule surcharge dans toute l'histoire est d'ajouter ENUMERATE_MEMBERS (m1,m2,m3...) à chaque structure, tout en implémentant une méthode spécifique par type de membre est un must dans n'importe quelle solution, donc je ne l'assume pas comme une surcharge.

Mise à jour: Il y a une implémentation très simple de la macro ENUMERATE_MEMBERS (mais elle pourrait être un peu étendue pour supporter l'héritage de enumerable struct)

#define ENUMERATE_MEMBERS(...) \
template<typename TEnumerator> inline void EnumerateWith(TEnumerator & enumerator) const { EnumerateWithHelper(enumerator, __VA_ARGS__ ); }\
template<typename TEnumerator> inline void EnumerateWith(TEnumerator & enumerator) { EnumerateWithHelper(enumerator, __VA_ARGS__); }

// EnumerateWithHelper
template<typename TEnumerator, typename ...T> inline void EnumerateWithHelper(TEnumerator & enumerator, T &...v) 
{ 
    int x[] = { (EnumerateWith(enumerator, v), 1)... }; 
}

// Generic EnumerateWith
template<typename TEnumerator, typename T>
auto EnumerateWith(TEnumerator & enumerator, T & val) -> std::void_t<decltype(val.EnumerateWith(enumerator))>
{
    val.EnumerateWith(enumerator);
}

Et vous n'avez pas besoin d'une bibliothèque tierce pour ces 15 lignes de code;)

1
répondu Evgeny Mamontov 2016-11-08 00:53:13

Le projet réflexe racine a un support pour cela.

Voir https://root.cern.ch/how/how-use-reflex

0
répondu Buğra Gedik 2016-06-17 02:15:34

Mise à Jour 24.2.2017

Auparavant, j'ai analysé le support pour l'utilisation de #define et comme il est recommandé dans certains articles web-j'ai frappé à travers les définitions dans visual C++ ne fonctionnaient pas identique à define utilisé dans gcc (par exemple, sur internet, cela est souvent appelé "walkaround MSVC"). En plus de ne pas être capable de comprendre facilement ce qui se passe derrière les machines d'expansion define / macro-il est plutôt difficile de déboguer chaque expansion macro.

Il existe plusieurs façons de contourner les complexités de l'expansion de define, une approche consiste à activer l'indicateur du compilateur "/P" (pré-processus pour déposer uniquement) - après cela, vous pouvez comparer la façon dont votre define est ouvert. (Auparavant, j'ai également utilisé activement l'opérateur stringfy (#))

J'ai rassemblé toutes les définitions utiles de plusieurs forums, les ai utilisées et commenté ce qui se passe derrière les machines, vous pouvez trouver un fichier d'en-tête entier ici maintenant:

Https://sourceforge.net/p/testcppreflect/code/HEAD/tree/MacroHelpers.h

Je pensais qu'il était plutôt trivial d'utiliser ces macros pour activer la réflexion C++, mais cela nécessite un peu plus de magie pour faire la réflexion.

Je me suis souvenu d'un exemple de code de travail, et le mettre comme projet sourceforge, peut être téléchargé ici:

Https://sourceforge.net/p/testcppreflect/code/HEAD/tree/

Le code de démonstration ressemble à ce:

#include "CppReflect.h"
using namespace std;


class Person
{
public:
    REFLECTABLE( Person,
        (CString)   name,
        (int)       age
    )

};

class People
{
public:
    REFLECTABLE( People,
        (CString) groupName,
        (vector<Person>)  people
    )
};


void main(void)
{
    People ppl;
    ppl.groupName = "Group1";

    Person p;
    p.name = L"Roger";
    p.age = 37;
    ppl.people.push_back(p);
    p.name = L"Alice";
    p.age = 27;
    ppl.people.push_back( p );
    p.name = L"Cindy";
    p.age = 17;
    ppl.people.push_back( p );

    CStringA xml = ToXML( &ppl );
    CStringW errors;

    People ppl2;

    FromXml( &ppl2, xml, errors );
    CStringA xml2 = ToXML( &ppl2 );
    printf( xml2 );

}

REFLECTABLE define utilise class name + field name Avec offsetof - pour identifier à quel endroit dans la mémoire un champ particulier est situé. J'ai essayé de ramasser la terminologie.net autant que possible, mais C++ et C# sont différents, donc ce n'est pas 1 à 1. Tout le modèle de réflexion C++ réside dans les classes TypeInfo et FieldInfo pour le timebeing, il est possible d'étendre le support aussi à la méthode, mais j'ai décidé de garder les choses simples pour l'instant.

J'ai utilisé Pugi xml parser pour récupérer code de démonstration en xml et le restaurer à partir de xml.

, Donc la sortie produite par le code de démonstration ressemble à ceci:

<?xml version="1.0" encoding="utf-8"?>
<People groupName="Group1">
    <people>
        <Person name="Roger" age="37" />
        <Person name="Alice" age="27" />
        <Person name="Cindy" age="17" />
    </people>
</People>

Il est également possible d'activer tout support de classe / structure 3-ème partie via la classe TypeTraits, et la spécification de Modèle partiel-pour définir votre propre classe TypeTraitsT, de la même manière que CString ou int - Voir exemple de code dans

Https://sourceforge.net/p/testcppreflect/code/HEAD/tree/TypeTraits.h#l65

template <>
class TypeTraitsT<CString> : public TypeTraits
{
public:
    virtual CStringW ToString( void* pField )
    { 
        CString* s = (CString*)pField;
        return *s;
    }

    virtual void FromString( void* pField, const wchar_t* value )
    { 
        CString* s = (CString*)pField;
        *s = value;
    }
};

template <>
class TypeTraitsT<int> : public TypeTraits
{
public:
    virtual CStringW ToString( void* pField )
    {
        int* p = (int*) pField;
        return std::to_string(*p).c_str();
    }

    virtual void FromString( void* pField, const wchar_t* value )
    {
        int* p = (int*)pField;
        *p = _wtoi(value);
    }
};

Je suppose que seul inconvénient de mon propre l'implémentation est l'utilisation de __if_exists - qui pourrait être une extension spécifique au compilateur Microsoft. Si quelqu'un sait comment marcher autour, faites le moi savoir.

0
répondu TarmoPikaro 2017-02-24 22:55:43

Le manque de réflexion intégrée en c++ est la seule raison pour laquelle le C++ moderne n'est pas utilisé pour le développement web (et manque D'ORM et d'autres frameworks)

Vous pouvez essayer http://www.extreme.indiana.edu/reflcpp/

-2
répondu vlad 2010-01-15 18:26:36

Un moyen simple consiste à utiliser l'opérateur dynamic_cast<>() qui, lorsqu'il est affecté à un mauvais type, renvoie NULL, de sorte que vous pouvez facilement passer à une classe concrète de base, en vérifiant la valeur du pointeur, s'il N'est pas NULL, le cast a été fait, et vous avez le type de l'objet.

Mais ce n'est qu'une solution simple, et elle ne fournit que le type des objets, vous ne pouvez pas demander quelles méthodes il a, comme en Java. Si vous avez besoin d'une solution avancée, il y a quelques frameworks à choisir.

-4
répondu lurks 2014-08-18 21:42:05