IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

 C++ Discussion :

Enum et méthode to_string


Sujet :

C++

  1. #1
    Membre confirmé
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Février 2013
    Messages
    84
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Février 2013
    Messages : 84
    Par défaut Enum et méthode to_string
    Bonjour,

    Je suis à la recherche de la méthode la plus simple de réaliser une enum class en c++11 qui possède une méthode to_string.
    Après plusieurs recherches sur internet je tombe sur plein de code qui sont plus ou moins simple. J'aimerai avoir votre avis.

    Voici ce que j'ai trouvé :
    https://stackoverflow.com/questions/...and-future-c20
    https://codereview.stackexchange.com...c-class-header

    Il y a plein de sujet mais aucunes méthodes semblent sortir du lot.

    Avez-vous un avis sur la question ?

    Personnellement je suis encore sur du C et j'aimerai faire quelque chose orienté C++ avec (enum class au moins)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    enum MyEnum {
        VALUE_1, VALUE_2, NB_MY_ENUM
    };
    static const char* MyEnumString[] = {
        "VALUE_1", "VALUE_2"
    };
    static_assert(sizeof(MyEnumString)/sizeof(char*) == NB_MY_ENUM, "MyEnumString array error");

  2. #2
    Membre Expert
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2011
    Messages
    760
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2011
    Messages : 760
    Par défaut
    La solution la plus simple de mon point de vue est d'utiliser une macro qui s'occupe de générer enum + correspondance valeur <-> string. C'est très simple lorsque l'enum est une suite continue de 0 à N (ce qui est le cas ici), et vraiment galère de faire une implémentation efficace avec des valeurs aléatoires (lorsque qu'on initialise les valeurs de l'enum) ou lorsqu'on veut "renommer" certaines correspondances. Perso, j'ai un truc qui le fait un peu près (sans valeur aléatoire, mais cela supporte plusieurs patterns d'incrémentation) et il y en a pour 500 lignes de macros bien dégoulinantes (alors que j'utilise BOOST_PP_* pour me simplifier la vie).

    Après il y a un moyen simple qui consiste à générer un switch (pas très dur avec boost.preprocessor). Mais sinon bettern-enums me semble un bon candidat pour une solution clef en main.

    Mais si l'enum existe déjà, alors il n'y a que la solution manuelle (ou avec un programme externe) en attendant des évolutions du langage (qui parle de méta-classe ?).

  3. #3
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 153
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 153
    Billets dans le blog
    4
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  4. #4
    Membre Expert
    Avatar de Pyramidev
    Homme Profil pro
    Tech Lead
    Inscrit en
    Avril 2016
    Messages
    1 513
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Tech Lead

    Informations forums :
    Inscription : Avril 2016
    Messages : 1 513
    Par défaut
    Bonjour,

    Voici une solution qui s'inspire beaucoup de la technique de Bousk, mais sans mettre l'énumération dans un fichier à part et sans faire de #undef :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
     
    #include <iostream>
    #include <stdexcept>
     
     
    #ifdef POUR_CHAQUE_JOUR_DE_LA_SEMAINE
    # error POUR_CHAQUE_JOUR_DE_LA_SEMAINE est déjà définie.
    #endif
    #define POUR_CHAQUE_JOUR_DE_LA_SEMAINE(callback) \
    	callback(Lundi)    \
    	callback(Mardi)    \
    	callback(Mercredi) \
    	callback(Jeudi)    \
    	callback(Vendredi) \
    	callback(Samedi)   \
    	callback(Dimanche)
     
     
    #ifdef EXPR_PUIS_VIRGULE
    # error EXPR_PUIS_VIRGULE est déjà définie.
    #endif
    #define EXPR_PUIS_VIRGULE(expr) expr,
     
    enum class JourDeLaSemaine {
    	POUR_CHAQUE_JOUR_DE_LA_SEMAINE(EXPR_PUIS_VIRGULE)
    };
     
     
    #ifdef IMPL_ToString_JourDeLaSemaine
    # error IMPL_ToString_JourDeLaSemaine est déjà définie.
    #endif
    #define IMPL_ToString_JourDeLaSemaine(expr) \
    	case JourDeLaSemaine::expr: return #expr;
     
    constexpr char const* ToString(JourDeLaSemaine param) {
    	switch(param) {
    		POUR_CHAQUE_JOUR_DE_LA_SEMAINE(IMPL_ToString_JourDeLaSemaine)
    		default: throw std::invalid_argument("Jour de la semaine invalide.");
    	}
    }
     
     
    #ifdef AFFICHER_JOUR_DE_LA_SEMAINE
    # error AFFICHER_JOUR_DE_LA_SEMAINE est déjà définie.
    #endif
    #define AFFICHER_JOUR_DE_LA_SEMAINE(expr) \
    	std::cout << ToString(JourDeLaSemaine::expr) << '\n';
     
    int main()
    {
    	POUR_CHAQUE_JOUR_DE_LA_SEMAINE(AFFICHER_JOUR_DE_LA_SEMAINE)
    	return 0;
    }

  5. #5
    Membre Expert
    Avatar de Pyramidev
    Homme Profil pro
    Tech Lead
    Inscrit en
    Avril 2016
    Messages
    1 513
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Tech Lead

    Informations forums :
    Inscription : Avril 2016
    Messages : 1 513
    Par défaut
    Pourquoi est-ce que je viens de me ramasser un -1 ?

    Dans la version de Bousk, conceptuellement, ENTRY est une variable globale muable. Au début, c'est une fonction qui prend un paramètre v et qui produit le code v,. Après, c'est une fonction qui prend un paramètre v et qui produit le code case v : return #v;.
    Conceptuellement, le fichier Enum.inl est une fonction sans paramètre qui produit du code. Le comportement de la fonction Enum.inl dépend de la variable globale ENTRY.

    Dans ma version, je suis partie de la solution de Bousk et j'ai éliminé la variable globale ENTRY. Pour cela, j'ai transformé Enum.inl en une fonction à un paramètre : POUR_CHAQUE_JOUR_DE_LA_SEMAINE(callback).

    Au niveau de l'architecture, c'est un peu plus propre.

  6. #6
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 153
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 153
    Billets dans le blog
    4
    Par défaut
    Plus propre en quoi ?
    Tu transformes un fichier très nettement identifiable comme une liste d'entrées et parfaitement lisible en un truc tordu. Les noms choisis n'aident en rien mais ça c'est à la limite pas le plus gros problème (je suis tout de même curieux de savoir comment tu ferais une liste d'une centaine d'entrées plus abstraite, disons de simples Actions. Et si tu as en plus plusieurs enums tu te retrouves avec un fichier de 1000 lignes de macro pour ça ?).
    Le code produit est bien plus long, moins lisible, avec les données cachées dans une macro dont le nom fait penser à une boucle for each et plus dans une liste lisible et aisément modifiable, et un système de callback qui fait un double dispatch qui rend le tout encore moins lisible parce que tu dois courir à 2 endroits chaque fois que tu veux comprendre ce qu'il se passe.
    Tout ça pour supprimer un fichier .inl et un #undef ? Bel exercice mais bof non merci je passe.
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  7. #7
    Membre Expert
    Avatar de Pyramidev
    Homme Profil pro
    Tech Lead
    Inscrit en
    Avril 2016
    Messages
    1 513
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Tech Lead

    Informations forums :
    Inscription : Avril 2016
    Messages : 1 513
    Par défaut
    Citation Envoyé par Bousk Voir le message
    Plus propre en quoi ?
    Tu transformes un fichier très nettement identifiable comme une liste d'entrées et parfaitement lisible en un truc tordu. [...]
    Le code produit est bien plus long, moins lisible, avec les données cachées dans une macro dont le nom fait penser à une boucle for each et plus dans une liste lisible et aisément modifiable, et un système de callback qui fait un double dispatch qui rend le tout encore moins lisible parce que tu dois courir à 2 endroits chaque fois que tu veux comprendre ce qu'il se passe.
    On n'a pas la même perception. Tu présentes ma solution comme vachement plus tordue et compliquée que la tienne alors que, à mes yeux, nos solutions sont de complexités égales, à part que ma solution est plus directe (un callback bien explicite) et la tienne plus étrange (un même fichier inclus plusieurs fois, mais avec un comportement différent à chaque fois).

    Dans nos deux solutions, on a des « données cachées ». Les miennes sont cachées dans une macro, les tiennes dans un fichier.

    Dans nos deux solutions, on doit « courir à 2 endroits ». Chez moi, c'est dans la définition de POUR_CHAQUE_JOUR_DE_LA_SEMAINE et dans celle de son argument. Chez toi, c'est dans le fichier Enum.inl et dans la définition du moment de la macro ENTRY.

    Dans nos deux solutions, on a la complexité d'une fonction qui appelle une fonction. Chez moi, POUR_CHAQUE_JOUR_DE_LA_SEMAINE appelle son paramètre qui change d'un appel à l'autre. Chez toi, Enum.inl appelle ENTRY qui change d'une inclusion à l'autre.

    Citation Envoyé par Bousk Voir le message
    (je suis tout de même curieux de savoir comment tu ferais une liste d'une centaine d'entrées plus abstraite, disons de simples Actions. Et si tu as en plus plusieurs enums tu te retrouves avec un fichier de 1000 lignes de macro pour ça ?).
    Non, si j'ai plusieurs énumérations très volumineuses, alors je découpe le code en plusieurs fichiers. Les macros n'obligent pas à écrire tout le code dans le même fichier.

    Citation Envoyé par Bousk Voir le message
    Tout ça pour supprimer un fichier .inl et un #undef ?
    À propos du fichier .inl :

    À la limite, si ton énumération a beaucoup de valeurs, alors découper le fichier en deux (Enum.hpp et Enum.inl) n'est pas gênant. Par contre, si elle ne contient qu'une dizaine de valeurs, alors c'est dommage de créer un nouveau fichier rien que pour ça.

    Si je pars de ta solution et que je ne fais que déplacer le #undef ENTRY puis transformer le fichier Enum.inl en macro, cela donne :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    #define ENUM_INL_AS_MACRO \
        ENTRY(Value1) \
        ENTRY(Value2)
     
    enum Enum {
    #define ENTRY(v) v,
    ENUM_INL_AS_MACRO
    #undef ENTRY
    };
    inline const char* ToString(Enum val) {
      switch(val) {
      #define ENTRY(v) case v : return #v;
      ENUM_INL_AS_MACRO
      #undef ENTRY
      default: return "<unknown>"; // ou throw std::invalid_argument("Valeur inconnue");
      }
    }
    On évite un aller-retour d'un fichier à l'autre.

    À propos du #undef :

    Si je fais une recherche de la chaîne undef dans tout le code source d'un projet, alors je vais tomber sur la liste des macros pour lesquelles la définition peut changer en cours d'exécution du préprocesseur. Par élimination, je saurai que toutes les autres macros seront immuables dans le sens où elles ne changeront pas d'état entre l'initialisation (avec #define) et l'utilisation. L'immuabilité est une garantie qui facilite l'analyse du code, surtout quand il s'agit de macros. Plus il y a d'occurrences de undef, plus ça pollue la recherche.

    En fait, je vois les #undef comme assez similaires aux const_cast, mais dans le monde des macros.

  8. #8
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 51
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Par défaut
    Citation Envoyé par Pyramidev Voir le message
    Si je fais une recherche de la chaîne undef dans tout le code source d'un projet, alors je vais tomber sur la liste des macros pour lesquelles la définition peut changer en cours d'exécution du préprocesseur. Par élimination, je saurai que toutes les autres macros seront immuables dans le sens où elles ne changeront pas d'état entre l'initialisation (avec #define) et l'utilisation. L'immuabilité est une garantie qui facilite l'analyse du code, surtout quand il s'agit de macros. Plus il y a d'occurrences de undef, plus ça pollue la recherche.
    Même si je peux comprendre ton raisonnement, je pense qu'il ne marcherait pas trop dans mon code. En effet, j'utilise très souvent undef dans mon code, mais pas pour changer la définition d'une macro, juste pour m'assurer que ma macro reste locale au fichier où elle se trouve, et n'ira jamais impacter de manière étrange d'autres fichiers ailleurs. Il s'agit typiquement d'un usage de undef qui disparaîtrait avec les modules (là où l'usage pour réutilisation resterait d'actualité).
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  9. #9
    Membre Expert
    Avatar de Pyramidev
    Homme Profil pro
    Tech Lead
    Inscrit en
    Avril 2016
    Messages
    1 513
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Tech Lead

    Informations forums :
    Inscription : Avril 2016
    Messages : 1 513
    Par défaut
    @JolyLoic : très bonne remarque. Vivement les modules !

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Erreur "class, interface or enum expected" pour une méthode main
    Par francky74 dans le forum Débuter avec Java
    Réponses: 4
    Dernier message: 28/01/2014, 21h20
  2. Réponses: 4
    Dernier message: 16/04/2012, 11h03
  3. La Classe Enum peut-elle contenir des méthodes
    Par devocx dans le forum Langage
    Réponses: 4
    Dernier message: 22/12/2009, 13h54
  4. enum avec attributs et méthodes et généralisation
    Par dummygreg dans le forum Concurrence et multi-thread
    Réponses: 16
    Dernier message: 03/07/2009, 13h49
  5. [java5] enums et méthodes fantomes
    Par Mucho dans le forum Langage
    Réponses: 5
    Dernier message: 25/06/2007, 10h07

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo