Existe-t-il un moyen simple de convertir une énumération C ++ en chaîne?

123

Supposons que nous ayons des énumérations nommées:

enum MyEnum {
      FOO,
      BAR = 0x50
};

Ce que j'ai recherché sur Google, c'est un script (n'importe quelle langue) qui analyse tous les en-têtes de mon projet et génère un en-tête avec une fonction par énumération.

char* enum_to_string(MyEnum t);

Et une implémentation avec quelque chose comme ça:

char* enum_to_string(MyEnum t){
      switch(t){
         case FOO:
            return "FOO";
         case BAR:
            return "BAR";
         default:
            return "INVALID ENUM";
      }
 }

Le gotcha est vraiment avec des énumérations typedefed et des énumérations de style C sans nom. Quelqu'un sait-il quelque chose à ce sujet?

EDIT: La solution ne doit pas modifier ma source, sauf pour les fonctions générées. Les énumérations sont dans une API, donc utiliser les solutions proposées jusqu'à présent n'est tout simplement pas une option.

Edu Felipe
la source
Réponse à propos de l'usine basée sur les macros déplacée dans stackoverflow.com/questions/147267/… - une fois la question mise à jour, elle n'est plus pertinente ici.
Suma

Réponses:

48

Vous voudrez peut-être consulter GCCXML .

L'exécution de GCCXML sur votre exemple de code produit:

<GCC_XML>
  <Namespace id="_1" name="::" members="_3 " mangled="_Z2::"/>
  <Namespace id="_2" name="std" context="_1" members="" mangled="_Z3std"/>
  <Enumeration id="_3" name="MyEnum" context="_1" location="f0:1" file="f0" line="1">
    <EnumValue name="FOO" init="0"/>
    <EnumValue name="BAR" init="80"/>
  </Enumeration>
  <File id="f0" name="my_enum.h"/>
</GCC_XML>

Vous pouvez utiliser n'importe quel langage que vous préférez pour extraire les balises Enumeration et EnumValue et générer le code souhaité.

Avdi
la source
Excellent! A travaillé comme un charme avec un simple script python. Merci.
Edu Felipe
6
+1, GCCXML est très joli! (Bien que j'aie presque mal interprété cela au départ comme une suggestion d'utiliser la syntaxe XML détaillée ci-dessus pour encoder votre enum - une solution qui sent la suringénierie!)
j_random_hacker
1
tout changement que vous pouvez publier le script python?
phillipwei
74

Les macros X sont la meilleure solution. Exemple:

#include <iostream>

enum Colours {
#   define X(a) a,
#   include "colours.def"
#   undef X
    ColoursCount
};

char const* const colours_str[] = {
#   define X(a) #a,
#   include "colours.def"
#   undef X
    0
};

std::ostream& operator<<(std::ostream& os, enum Colours c)
{
    if (c >= ColoursCount || c < 0) return os << "???";
    return os << colours_str[c];
}

int main()
{
    std::cout << Red << Blue << Green << Cyan << Yellow << Magenta << std::endl;
}

colours.def:

X(Red)
X(Green)
X(Blue)
X(Cyan)
X(Yellow)
X(Magenta)

Cependant, je préfère généralement la méthode suivante, afin qu'il soit possible de modifier un peu la chaîne.

#define X(a, b) a,
#define X(a, b) b,

X(Red, "red")
X(Green, "green")
// etc.
Marcin Koziuk
la source
11
astucieux, même si je n'aime pas le fichier supplémentaire
Ronny Brendel
2
Assurez-vous simplement que votre processus de construction n'ajoute pas #pragma (une fois) avant chaque fichier d'inclusion ...
xtofl
24
Je ne suis pas sûr de la "meilleure" solution!
Courses de légèreté en orbite le
2
Cette solution est tout simplement bien supérieure à n'importe quel cas de commutateur ou à base de tableau, car elle ne duplique pas les noms, ce qui facilite la modification de l'énumération.
Julien Guertault
2
@ ikku100 vous vous trompez #define X(a, b) #b. Cela n'est nécessaire que si la définition ressemble à ceci X(Red, red), plutôt que la définition montrée dans la réponse,X(Red, "red")
learnvst
43

@hydroo: Sans le fichier supplémentaire:

#define SOME_ENUM(DO) \
    DO(Foo) \
    DO(Bar) \
    DO(Baz)

#define MAKE_ENUM(VAR) VAR,
enum MetaSyntacticVariable{
    SOME_ENUM(MAKE_ENUM)
};

#define MAKE_STRINGS(VAR) #VAR,
const char* const MetaSyntacticVariableNames[] = {
    SOME_ENUM(MAKE_STRINGS)
};
Jasper Bekkers
la source
J'adore cette solution. Ce serait plus clair si SOME_UNION et MAKE_UNION étaient appelés SOME_ENUM et MAKE_ENUM, cependant.
Bruno Martinez
C'est une excellente solution. J'ai le gestionnaire de ressources C ++ le plus maintenable que j'ai jamais traité.
DCurro
Je dois vous remercier pour cette solution simple :-) - Je l'ai cependant un peu modifiée, pour MetaSyntacticVariableNames[]faire partie d'une déclaration de classe, en créant une méthodestatic const char* getNameByEnum(MetaSyntacticVariable e) { /*code to return the static string*/ }
DeckerDK
Réponse fantastique! Je l'ai simplifié davantage en regroupant MAKE_ENUM et MAKE_STRINGS dans une seule macro, ce qui rend l'ensemble du processus encore plus simple. J'ai ajouté une réponse dans ce fil avec ce code si quelqu'un est intéressé.
Francois Bertrand
35

Ce que j'ai tendance à faire est de créer un tableau C avec les noms dans le même ordre et la même position que les valeurs d'énumération.

par exemple.

enum colours { red, green, blue };
const char *colour_names[] = { "red", "green", "blue" };

alors vous pouvez utiliser le tableau aux endroits où vous voulez une valeur lisible par l'homme, par exemple

colours mycolour = red;
cout << "the colour is" << colour_names[mycolour];

Vous pouvez expérimenter un peu avec l'opérateur de stringizing (voir # dans votre référence de préprocesseur) qui fera ce que vous voulez, dans certaines circonstances - par exemple:

#define printword(XX) cout << #XX;
printword(red);

affichera "rouge" sur stdout. Malheureusement, cela ne fonctionnera pas pour une variable (car vous obtiendrez le nom de la variable imprimé)

gbjbaanb
la source
La dernière mise en garde (ne fonctionnera pas pour une variable) est un gros inconvénient, mais +1 quand même.
chappjc
3
Fonctionne uniquement si vous ne définissez pas de valeurs numériques spéciales pour énumérer les entrées.
kyb
11

J'ai une macro incroyablement simple à utiliser qui le fait de manière complètement sèche. Cela implique des macros variadiques et une simple magie d'analyse. Voici:

#define AWESOME_MAKE_ENUM(name, ...) enum class name { __VA_ARGS__, __COUNT}; \
inline std::ostream& operator<<(std::ostream& os, name value) { \
std::string enumName = #name; \
std::string str = #__VA_ARGS__; \
int len = str.length(); \
std::vector<std::string> strings; \
std::ostringstream temp; \
for(int i = 0; i < len; i ++) { \
if(isspace(str[i])) continue; \
        else if(str[i] == ',') { \
        strings.push_back(temp.str()); \
        temp.str(std::string());\
        } \
        else temp<< str[i]; \
} \
strings.push_back(temp.str()); \
os << enumName << "::" << strings[static_cast<int>(value)]; \
return os;} 

Pour utiliser ceci dans votre code, faites simplement:

AWESOME_MAKE_ENUM(Animal,
    DOG,
    CAT,
    HORSE
);
Debdatta Basu
la source
1
Bonne idée en utilisant une énumération fortement typée (classe enum). Voici une démo: cpp.sh/4ife
chappjc
Cela fonctionne-t-il avec des énumérations / symboles définis en externe? Par exemple, les symboles définis par le système d'exploitation ou par la bibliothèque avec des espaces dans la numérotation?
Jason Harrison
Très bien, mais ne compile pas s'il est placé dans une classe (je ne pouvais pas comprendre pourquoi).
AlwaysLearning
Je n'ai pas pu obtenir cela pour compiler dans VS2015. J'obtiens un avertissement et une erreur: avertissement: commentaire multiligne [-Wcomment] #define MAKE_ENUM (nom, ...) enum nom de classe { VA_ARGS , __COUNT} erreur: erroné '#' dans le programme std *: string enumName = #name
Craig.Feied
8

QT est capable d'extraire celui de (grâce au compilateur de méta-objets):

QNetworkReply::NetworkError error;

error = fetchStuff();

if (error != QNetworkReply::NoError) {

    QString errorValue;

    QMetaObject meta = QNetworkReply::staticMetaObject;

    for (int i=0; i < meta.enumeratorCount(); ++i) {

        QMetaEnum m = meta.enumerator(i);

        if (m.name() == QLatin1String("NetworkError")) {

            errorValue = QLatin1String(m.valueToKey(error));

            break;

        }

    }

    QMessageBox box(QMessageBox::Information, "Failed to fetch",

                "Fetching stuff failed with error '%1`").arg(errorValue),

                QMessageBox::Ok);

    box.exec();

    return 1;

}

Dans Qt, chaque classe qui a la macro Q_OBJECT aura automatiquement un membre statique "staticMetaObject" de type QMetaObject. Vous pouvez ensuite trouver toutes sortes de choses intéressantes comme les propriétés, les signaux, les slots et même les énumérations.

La source

Ronny Brendel
la source
7

Cela peut être fait en C ++ 11

#include <map>
enum MyEnum { AA, BB, CC, DD };

static std::map< MyEnum, const char * > info = {
   {AA, "This is an apple"},
   {BB, "This is a book"},
   {CC, "This is a coffee"},
   {DD, "This is a door"}
};

void main()
{
    std::cout << info[AA] << endl
              << info[BB] << endl
              << info[CC] << endl
              << info[DD] << endl;
}
serge
la source
1
Cela ne répond pas à la question de l'OP: il cherchait un moyen de générer automatiquement une fonction pour renvoyer le nom d'un membre enum sous forme de chaîne.
Spooky
7

Je viens de réinventer cette roue aujourd'hui et j'ai pensé la partager.

Cette mise en œuvre ne pas besoin de modifier le code qui définit les constantes qui peuvent être énumérations ou #defines ou toute autre chose qui échoit un entier - dans mon cas , j'avais des symboles définis en termes d'autres symboles. Cela fonctionne également bien avec des valeurs clairsemées. Il autorise même plusieurs noms pour la même valeur, renvoyant toujours le premier. Le seul inconvénient est que cela vous oblige à créer un tableau des constantes, qui pourraient devenir obsolètes lorsque de nouvelles sont ajoutées par exemple.

struct IdAndName
{
   int          id;
   const char * name;
   bool operator<(const IdAndName &rhs) const { return id < rhs.id; }
};
#define ID_AND_NAME(x) { x, #x }

const char * IdToName(int id, IdAndName *table_begin, IdAndName *table_end)
{
   if ((table_end - table_begin) > 1 && table_begin[0].id > table_begin[1].id)
      std::stable_sort(table_begin, table_end);

   IdAndName searchee = { id, NULL };
   IdAndName *p = std::lower_bound(table_begin, table_end, searchee);
   return (p == table_end || p->id != id) ? NULL : p->name;
}

template<int N>
const char * IdToName(int id, IdAndName (&table)[N])
{
   return IdToName(id, &table[0], &table[N]);
}

Un exemple de la façon dont vous l'utiliseriez:

static IdAndName WindowsErrorTable[] =
{
   ID_AND_NAME(INT_MAX),               // flag value to indicate unsorted table
   ID_AND_NAME(NO_ERROR),
   ID_AND_NAME(ERROR_INVALID_FUNCTION),
   ID_AND_NAME(ERROR_FILE_NOT_FOUND),
   ID_AND_NAME(ERROR_PATH_NOT_FOUND),
   ID_AND_NAME(ERROR_TOO_MANY_OPEN_FILES),
   ID_AND_NAME(ERROR_ACCESS_DENIED),
   ID_AND_NAME(ERROR_INVALID_HANDLE),
   ID_AND_NAME(ERROR_ARENA_TRASHED),
   ID_AND_NAME(ERROR_NOT_ENOUGH_MEMORY),
   ID_AND_NAME(ERROR_INVALID_BLOCK),
   ID_AND_NAME(ERROR_BAD_ENVIRONMENT),
   ID_AND_NAME(ERROR_BAD_FORMAT),
   ID_AND_NAME(ERROR_INVALID_ACCESS),
   ID_AND_NAME(ERROR_INVALID_DATA),
   ID_AND_NAME(ERROR_INVALID_DRIVE),
   ID_AND_NAME(ERROR_CURRENT_DIRECTORY),
   ID_AND_NAME(ERROR_NOT_SAME_DEVICE),
   ID_AND_NAME(ERROR_NO_MORE_FILES)
};

const char * error_name = IdToName(GetLastError(), WindowsErrorTable);

La IdToNamefonction s'appuie sur std::lower_bounddes recherches rapides, ce qui nécessite le tri de la table. Si les deux premières entrées du tableau sont dans le désordre, la fonction le triera automatiquement.

Edit: Un commentaire m'a fait penser à une autre façon d'utiliser le même principe. Une macro simplifie la génération d'une grande switchinstruction.

#define ID_AND_NAME(x) case x: return #x

const char * WindowsErrorToName(int id)
{
    switch(id)
    {
        ID_AND_NAME(ERROR_INVALID_FUNCTION);
        ID_AND_NAME(ERROR_FILE_NOT_FOUND);
        ID_AND_NAME(ERROR_PATH_NOT_FOUND);
        ID_AND_NAME(ERROR_TOO_MANY_OPEN_FILES);
        ID_AND_NAME(ERROR_ACCESS_DENIED);
        ID_AND_NAME(ERROR_INVALID_HANDLE);
        ID_AND_NAME(ERROR_ARENA_TRASHED);
        ID_AND_NAME(ERROR_NOT_ENOUGH_MEMORY);
        ID_AND_NAME(ERROR_INVALID_BLOCK);
        ID_AND_NAME(ERROR_BAD_ENVIRONMENT);
        ID_AND_NAME(ERROR_BAD_FORMAT);
        ID_AND_NAME(ERROR_INVALID_ACCESS);
        ID_AND_NAME(ERROR_INVALID_DATA);
        ID_AND_NAME(ERROR_INVALID_DRIVE);
        ID_AND_NAME(ERROR_CURRENT_DIRECTORY);
        ID_AND_NAME(ERROR_NOT_SAME_DEVICE);
        ID_AND_NAME(ERROR_NO_MORE_FILES);
        default: return NULL;
    }
}
Mark Ransom
la source
Bonne solution. Mais pour moi, je préfère switch and casecar c'est simple et facile à comprendre.
Deqing
6
#define stringify( name ) # name

enum MyEnum {
    ENUMVAL1
};
...stuff...

stringify(EnumName::ENUMVAL1);  // Returns MyEnum::ENUMVAL1

Discussion supplémentaire sur cette méthode

Astuces de directive de préprocesseur pour les nouveaux arrivants

Ben
la source
4
En fait, cela est assez inutile, car la méthode stringify est au moment de la compilation et est assez littérale. Si vous dites avoir le type enum en question dans une variable, essayer de stringifier la variable vous donnera simplement le nom de la variable, pas le nom du type enum.
srcspider
5

Intéressant de voir le nombre de façons. en voici un que j'ai utilisé il y a longtemps:

dans le fichier myenummap.h:

#include <map>
#include <string>
enum test{ one, two, three, five=5, six, seven };
struct mymap : std::map<unsigned int, std::string>
{
  mymap()
  {
    this->operator[]( one ) = "ONE";
    this->operator[]( two ) = "TWO";
    this->operator[]( three ) = "THREE";
    this->operator[]( five ) = "FIVE";
    this->operator[]( six ) = "SIX";
    this->operator[]( seven ) = "SEVEN";
  };
  ~mymap(){};
};

dans main.cpp

#include "myenummap.h"

...
mymap nummap;
std::cout<< nummap[ one ] << std::endl;

Ce n'est pas un const, mais c'est pratique.

Voici une autre façon d'utiliser les fonctionnalités de C ++ 11. Ceci est const, n'hérite pas d'un conteneur STL et est un peu plus ordonné:

#include <vector>
#include <string>
#include <algorithm>
#include <iostream>

//These stay together and must be modified together
enum test{ one, two, three, five=5, six, seven };
std::string enum_to_str(test const& e)
{
    typedef std::pair<int,std::string> mapping;
    auto m = [](test const& e,std::string const& s){return mapping(static_cast<int>(e),s);}; 
    std::vector<mapping> const nummap = 
    { 
        m(one,"one"), 
        m(two,"two"), 
        m(three,"three"),
        m(five,"five"),
        m(six,"six"),
        m(seven,"seven"),
    };
    for(auto i  : nummap)
    {
        if(i.first==static_cast<int>(e))
        {
            return i.second;
        }
    }
    return "";
}

int main()
{
//  std::cout<< enum_to_str( 46 ) << std::endl; //compilation will fail
    std::cout<< "Invalid enum to string : [" << enum_to_str( test(46) ) << "]"<<std::endl; //returns an empty string
    std::cout<< "Enumval five to string : ["<< enum_to_str( five ) << "] "<< std::endl; //works
    return 0;
}
Carl
la source
1
C'est parfaitement légal. Je le fais tout le temps.
Jonathan Graehl
Bonne solution. Ceci est C ++ donc utiliser stl map est correct.
Adam Bruss
4
#include <stdarg.h>
#include <algorithm>
#include <string> 
#include <vector>
#include <sstream>
#include <map>

#define SMART_ENUM(EnumName, ...)                                   \
class EnumName                                                      \
{                                                                   \
private:                                                            \
    static std::map<int, std::string> nameMap;                      \
public:                                                             \
    enum {__VA_ARGS__};                                             \
private:                                                            \
    static std::map<int, std::string> initMap()                     \
    {                                                               \
        using namespace std;                                        \
                                                                    \
        int val = 0;                                                \
        string buf_1, buf_2, str = #__VA_ARGS__;                    \
        replace(str.begin(), str.end(), '=', ' ');                  \
        stringstream stream(str);                                   \
        vector<string> strings;                                     \
        while (getline(stream, buf_1, ','))                         \
            strings.push_back(buf_1);                               \
        map<int, string> tmp;                                       \
        for(vector<string>::iterator it = strings.begin();          \
                                               it != strings.end(); \
                                               ++it)                \
        {                                                           \
            buf_1.clear(); buf_2.clear();                           \
            stringstream localStream(*it);                          \
            localStream>> buf_1 >> buf_2;                           \
            if(buf_2.size() > 0)                                    \
                val = atoi(buf_2.c_str());                          \
            tmp[val++] = buf_1;                                     \
        }                                                           \
        return tmp;                                                 \
    }                                                               \
public:                                                             \
    static std::string toString(int aInt)                           \
    {                                                               \
        return nameMap[aInt];                                       \
    }                                                               \
};                                                                  \
std::map<int, std::string>                                          \
EnumName::nameMap = EnumName::initMap();

Usage:

SMART_ENUM(MyEnum, ONE=1, TWO, THREE, TEN=10, ELEVEN)
cout<<MyEnum::toString(MyEnum::TWO);
cout<<MyEnum::toString(10);

la source
1
J'aime votre API, mais malheureusement, votre SmartEnum ne crée pas réellement un "type" d'énumération. Tu ne peux pas faire MyEnum x = MyEnum::TWO;. J'ai publié ma modification de votre classe pour soutenir cela.
Mark Lakata
4

La solution macro de Suma est bien. Cependant, vous n'avez pas besoin d'avoir deux macros différentes. C ++ inclura heureusement un en-tête deux fois. Laissez de côté la garde d'inclusion.

Vous auriez donc un foobar.h définissant juste

ENUM(Foo, 1)
ENUM(Bar, 2)

et vous l'incluez comme ceci:

#define ENUMFACTORY_ARGUMENT "foobar.h"
#include "enumfactory.h"

enumfactory.h fera 2 #include ENUMFACTORY_ARGUMENTs. Au premier tour, il étend ENUM comme celui de Suma DECLARE_ENUM; au deuxième tour, ENUM fonctionne comme DEFINE_ENUM.

Vous pouvez également inclure enumfactory.h plusieurs fois, à condition de transmettre différents # define pour ENUMFACTORY_ARGUMENT

MSalters
la source
semble que suma a déplacé la réponse ici . Vous voudrez peut-être inclure le lien dans votre réponse. Je n'ai trouvé le commentaire que par hasard et sans réponse de sumas, celui-ci est plutôt inutile
idclev 463035818
3

Notez que votre fonction de conversion devrait idéalement renvoyer un const char *.

Si vous pouvez vous permettre de mettre vos énumérations dans leurs fichiers d'en-tête séparés, vous pouvez peut-être faire quelque chose comme ça avec des macros (oh, ce sera moche):

#include "enum_def.h"
#include "colour.h"
#include "enum_conv.h"
#include "colour.h"

Où enum_def.h a:

#undef ENUM_START
#undef ENUM_ADD
#undef ENUM_END
#define ENUM_START(NAME) enum NAME {
#define ENUM_ADD(NAME, VALUE) NAME = VALUE,
#define ENUM_END };

Et enum_conv.h a:

#undef ENUM_START
#undef ENUM_ADD
#undef ENUM_END
#define ENUM_START(NAME) const char *##NAME##_to_string(NAME val) { switch (val) {
#define ENUM_ADD(NAME, VALUE) case NAME: return #NAME;
#define ENUM_END default: return "Invalid value"; } }

Et enfin, colour.h a:

ENUM_START(colour)
ENUM_ADD(red,   0xff0000)
ENUM_ADD(green, 0x00ff00)
ENUM_ADD(blue,  0x0000ff)
ENUM_END

Et vous pouvez utiliser la fonction de conversion comme:

printf("%s", colour_to_string(colour::red));

C'est moche, mais c'est le seul moyen (au niveau du préprocesseur) qui vous permet de définir votre énumération à un seul endroit de votre code. Votre code n'est donc pas sujet à des erreurs dues à des modifications de l'énumération. Votre définition d'énumération et la fonction de conversion seront toujours synchronisées. Cependant, je le répète, c'est moche :)

Ates Goral
la source
3

Une autre réponse: dans certains contextes, il est logique de définir votre énumération dans un format non-code, comme un fichier CSV, YAML ou XML, puis de générer à la fois le code d'énumération C ++ et le code de chaîne à partir de la définition. Cette approche peut être pratique ou non dans votre application, mais c'est quelque chose à garder à l'esprit.

Avdi
la source
3

Ceci est une modification de la réponse @ user3360260. Il a les nouvelles fonctionnalités suivantes

  • MyEnum fromString(const string&) soutien
  • compile avec VisualStudio 2012
  • l'énumération est un type POD réel (pas seulement des déclarations const), vous pouvez donc l'affecter à une variable.
  • Ajout de la fonctionnalité "range" C ++ (sous forme de vecteur) pour permettre l'itération "foreach" sur enum

Usage:

SMART_ENUM(MyEnum, ONE=1, TWO, THREE, TEN=10, ELEVEN)
MyEnum foo = MyEnum::TWO;
cout << MyEnum::toString(foo);  // static method
cout << foo.toString();         // member method
cout << MyEnum::toString(MyEnum::TWO);
cout << MyEnum::toString(10);
MyEnum foo = myEnum::fromString("TWO");

// C++11 iteration over all values
for( auto x : MyEnum::allValues() )
{
  cout << x.toString() << endl;
}

Voici le code

#define SMART_ENUM(EnumName, ...)                                   \
class EnumName                                                      \
{                                                                   \
public:                                                             \
    EnumName() : value(0) {}                                        \
    EnumName(int x) : value(x) {}                                   \
public:                                                             \
    enum {__VA_ARGS__};                                             \
private:                                                            \
    static void initMap(std::map<int, std::string>& tmp)                     \
    {                                                               \
        using namespace std;                                        \
                                                                    \
        int val = 0;                                                \
        string buf_1, buf_2, str = #__VA_ARGS__;                    \
        replace(str.begin(), str.end(), '=', ' ');                  \
        stringstream stream(str);                                   \
        vector<string> strings;                                     \
        while (getline(stream, buf_1, ','))                         \
            strings.push_back(buf_1);                               \
        for(vector<string>::iterator it = strings.begin();          \
                                                it != strings.end(); \
                                                ++it)                \
        {                                                           \
            buf_1.clear(); buf_2.clear();                           \
            stringstream localStream(*it);                          \
            localStream>> buf_1 >> buf_2;                           \
            if(buf_2.size() > 0)                                    \
                val = atoi(buf_2.c_str());                          \
            tmp[val++] = buf_1;                                     \
        }                                                           \
    }                                                               \
    int value;                                                      \
public:                                                             \
    operator int () const { return value; }                         \
    std::string toString(void) const {                              \
            return toString(value);                                 \
    }                                                               \
    static std::string toString(int aInt)                           \
    {                                                               \
        return nameMap()[aInt];                                     \
    }                                                               \
    static EnumName fromString(const std::string& s)                \
    {                                                               \
        auto it = find_if(nameMap().begin(), nameMap().end(), [s](const std::pair<int,std::string>& p) { \
            return p.second == s;                                   \
        });                                                         \
        if (it == nameMap().end()) {                                \
        /*value not found*/                                         \
            throw EnumName::Exception();                            \
        } else {                                                    \
            return EnumName(it->first);                             \
        }                                                           \
    }                                                               \
    class Exception : public std::exception {};                     \
    static std::map<int,std::string>& nameMap() {                   \
      static std::map<int,std::string> nameMap0;                    \
      if (nameMap0.size() ==0) initMap(nameMap0);                   \
      return nameMap0;                                              \
    }                                                               \
    static std::vector<EnumName> allValues() {                      \
      std::vector<EnumName> x{ __VA_ARGS__ };                       \
      return x;                                                     \
    }                                                               \
    bool operator<(const EnumName a) const { return (int)*this < (int)a; } \
};         

Notez que la conversion toString est une recherche rapide, tandis que la conversion fromString est une recherche linéaire lente. Mais les chaînes sont si chères de toute façon (et le fichier associé IO), je n'ai pas ressenti le besoin d'optimiser ou d'utiliser une bimap.

Mark Lakata
la source
Vous et user3360260 avez une bonne solution. Pourquoi ne pas avoir un multimap à la place?
Vincent le
3

Voici une solution à un fichier (basée sur une réponse élégante de @Marcin:

#include <iostream>

#define ENUM_TXT \
X(Red) \
X(Green) \
X(Blue) \
X(Cyan) \
X(Yellow) \
X(Magenta) \

enum Colours {
#   define X(a) a,
ENUM_TXT
#   undef X
    ColoursCount
};

char const* const colours_str[] = {
#   define X(a) #a,
ENUM_TXT
#   undef X
    0
};

std::ostream& operator<<(std::ostream& os, enum Colours c)
{
    if (c >= ColoursCount || c < 0) return os << "???";
    return os << colours_str[c] << std::endl;
}

int main()
{
    std::cout << Red << Blue << Green << Cyan << Yellow << Magenta << std::endl;
}
FractalSpace
la source
2

Je fais cela avec des classes wrapper enum côte à côte séparées qui sont générées avec des macros. Il y a plusieurs avantages:

  • Peut les générer pour des énumérations que je ne définis pas (par exemple: énumérations d'en-tête de plate-forme OS)
  • Peut incorporer la vérification de plage dans la classe wrapper
  • Peut faire un formatage "plus intelligent" avec des énumérations de champs de bits

L'inconvénient, bien sûr, est que je dois dupliquer les valeurs d'énumération dans les classes du formateur, et je n'ai aucun script pour les générer. À part cela, cependant, cela semble fonctionner plutôt bien.

Voici un exemple d'énumération de ma base de code, sans tout le code du framework qui implémente les macros et les modèles, mais vous pouvez avoir l'idée:

enum EHelpLocation
{
    HELP_LOCATION_UNKNOWN   = 0, 
    HELP_LOCAL_FILE         = 1, 
    HELP_HTML_ONLINE        = 2, 
};
class CEnumFormatter_EHelpLocation : public CEnumDefaultFormatter< EHelpLocation >
{
public:
    static inline CString FormatEnum( EHelpLocation eValue )
    {
        switch ( eValue )
        {
            ON_CASE_VALUE_RETURN_STRING_OF_VALUE( HELP_LOCATION_UNKNOWN );
            ON_CASE_VALUE_RETURN_STRING_OF_VALUE( HELP_LOCAL_FILE );
            ON_CASE_VALUE_RETURN_STRING_OF_VALUE( HELP_HTML_ONLINE );
        default:
            return FormatAsNumber( eValue );
        }
    }
};
DECLARE_RANGE_CHECK_CLASS( EHelpLocation, CRangeInfoSequential< HELP_HTML_ONLINE > );
typedef ESmartEnum< EHelpLocation, HELP_LOCATION_UNKNOWN, CEnumFormatter_EHelpLocation, CRangeInfo_EHelpLocation > SEHelpLocation;

L'idée est alors au lieu d'utiliser EHelpLocation, vous utilisez SEHelpLocation; tout fonctionne de la même manière, mais vous obtenez une vérification de plage et une méthode 'Format ()' sur la variable enum elle-même. Si vous devez formater une valeur autonome, vous pouvez utiliser CEnumFormatter_EHelpLocation :: FormatEnum (...).

J'espère que cela est utile. Je me rends compte que cela ne répond pas non plus à la question originale sur un script pour générer réellement l'autre classe, mais j'espère que la structure aidera quelqu'un à essayer de résoudre le même problème ou à écrire un tel script.

pseudo
la source
2

C'est un logiciel inédit, mais il semble que BOOST_ENUM de Frank Laub pourrait faire l'affaire. La partie que j'aime à ce sujet est que vous pouvez définir une énumération dans le cadre d'une classe, ce que la plupart des énumérations basées sur les macros ne vous permettent généralement pas de faire. Il est situé dans le Boost Vault à l' adresse : http://www.boostpro.com/vault/index.php?action=downloadfile&filename=enum_rev4.6.zip&directory=& Il n'a vu aucun développement depuis 2006, donc je ne le fais pas savoir à quel point il se compile avec les nouvelles versions de Boost. Regardez sous libs / test pour un exemple d'utilisation.

Alexis
la source
2

C'était ma solution avec BOOST:

#include <boost/preprocessor.hpp>

#define X_STR_ENUM_TOSTRING_CASE(r, data, elem)                                 \
    case elem : return BOOST_PP_STRINGIZE(elem);

#define X_ENUM_STR_TOENUM_IF(r, data, elem)                                     \
    else if(data == BOOST_PP_STRINGIZE(elem)) return elem;

#define STR_ENUM(name, enumerators)                                             \
    enum name {                                                                 \
        BOOST_PP_SEQ_ENUM(enumerators)                                          \
    };                                                                          \
                                                                                \
    inline const QString enumToStr(name v)                                      \
    {                                                                           \
        switch (v)                                                              \
        {                                                                       \
            BOOST_PP_SEQ_FOR_EACH(                                              \
                X_STR_ENUM_TOSTRING_CASE,                                       \
                name,                                                           \
                enumerators                                                     \
            )                                                                   \
                                                                                \
            default:                                                            \
                return "[Unknown " BOOST_PP_STRINGIZE(name) "]";                \
        }                                                                       \
    }                                                                           \
                                                                                \
    template <typename T>                                                       \
    inline const T strToEnum(QString v);                                        \
                                                                                \
    template <>                                                                 \
    inline const name strToEnum(QString v)                                      \
    {                                                                           \
        if(v=="")                                                               \
            throw std::runtime_error("Empty enum value");                       \
                                                                                \
        BOOST_PP_SEQ_FOR_EACH(                                                  \
            X_ENUM_STR_TOENUM_IF,                                               \
            v,                                                                  \
            enumerators                                                         \
        )                                                                       \
                                                                                \
        else                                                                    \
            throw std::runtime_error(                                           \
                        QString("[Unknown value %1 for enum %2]")               \
                            .arg(v)                                             \
                            .arg(BOOST_PP_STRINGIZE(name))                      \
                                .toStdString().c_str());                        \
    }

Pour créer une énumération, déclarez:

STR_ENUM
(
    SERVICE_RELOAD,
        (reload_log)
        (reload_settings)
        (reload_qxml_server)
)

Pour les conversions:

SERVICE_RELOAD serviceReloadEnum = strToEnum<SERVICE_RELOAD>("reload_log");
QString serviceReloadStr = enumToStr(reload_log);
lopes
la source
2

Je veux publier ceci au cas où quelqu'un le trouverait utile.

Dans mon cas, j'ai simplement besoin de générer ToString()et de FromString()fonctionner pour une seule énumération C ++ 11 à partir d'un seul .hppfichier.

J'ai écrit un script python qui analyse le fichier d'en-tête contenant les éléments d'énumération et génère les fonctions dans un nouveau .cppfichier.

Vous pouvez ajouter ce script dans CMakeLists.txt avec execute_process ou en tant qu'événement de pré-génération dans Visual Studio. Le .cppfichier sera généré automatiquement, sans qu'il soit nécessaire de le mettre à jour manuellement à chaque fois qu'un nouvel élément d'énumération est ajouté.

generate_enum_strings.py

# This script is used to generate strings from C++ enums

import re
import sys
import os

fileName = sys.argv[1]
enumName = os.path.basename(os.path.splitext(fileName)[0])

with open(fileName, 'r') as f:
    content = f.read().replace('\n', '')

searchResult = re.search('enum(.*)\{(.*?)\};', content)
tokens = searchResult.group(2)
tokens = tokens.split(',')
tokens = map(str.strip, tokens)
tokens = map(lambda token: re.search('([a-zA-Z0-9_]*)', token).group(1), tokens)

textOut = ''
textOut += '\n#include "' + enumName + '.hpp"\n\n'
textOut += 'namespace myns\n'
textOut += '{\n'
textOut += '    std::string ToString(ErrorCode errorCode)\n'
textOut += '    {\n'
textOut += '        switch (errorCode)\n'
textOut += '        {\n'

for token in tokens:
    textOut += '        case ' + enumName + '::' + token + ':\n'
    textOut += '            return "' + token + '";\n'

textOut += '        default:\n'
textOut += '            return "Last";\n'
textOut += '        }\n'
textOut += '    }\n'
textOut += '\n'
textOut += '    ' + enumName + ' FromString(const std::string &errorCode)\n'
textOut += '    {\n'
textOut += '        if ("' + tokens[0] + '" == errorCode)\n'
textOut += '        {\n'
textOut += '            return ' + enumName + '::' + tokens[0] + ';\n'
textOut += '        }\n'

for token in tokens[1:]:
    textOut += '        else if("' + token + '" == errorCode)\n'
    textOut += '        {\n'
    textOut += '            return ' + enumName + '::' + token + ';\n'
    textOut += '        }\n'

textOut += '\n'
textOut += '        return ' + enumName + '::Last;\n'
textOut += '    }\n'
textOut += '}\n'

fileOut = open(enumName + '.cpp', 'w')
fileOut.write(textOut)

Exemple:

ErrorCode.hpp

#pragma once

#include <string>
#include <cstdint>

namespace myns
{
    enum class ErrorCode : uint32_t
    {
        OK = 0,
        OutOfSpace,
        ConnectionFailure,
        InvalidJson,
        DatabaseFailure,
        HttpError,
        FileSystemError,
        FailedToEncrypt,
        FailedToDecrypt,
        EndOfFile,
        FailedToOpenFileForRead,
        FailedToOpenFileForWrite,
        FailedToLaunchProcess,

        Last
    };

    std::string ToString(ErrorCode errorCode);
    ErrorCode FromString(const std::string &errorCode);
}

Courir python generate_enum_strings.py ErrorCode.hpp

Résultat:

ErrorCode.cpp

#include "ErrorCode.hpp"

namespace myns
{
    std::string ToString(ErrorCode errorCode)
    {
        switch (errorCode)
        {
        case ErrorCode::OK:
            return "OK";
        case ErrorCode::OutOfSpace:
            return "OutOfSpace";
        case ErrorCode::ConnectionFailure:
            return "ConnectionFailure";
        case ErrorCode::InvalidJson:
            return "InvalidJson";
        case ErrorCode::DatabaseFailure:
            return "DatabaseFailure";
        case ErrorCode::HttpError:
            return "HttpError";
        case ErrorCode::FileSystemError:
            return "FileSystemError";
        case ErrorCode::FailedToEncrypt:
            return "FailedToEncrypt";
        case ErrorCode::FailedToDecrypt:
            return "FailedToDecrypt";
        case ErrorCode::EndOfFile:
            return "EndOfFile";
        case ErrorCode::FailedToOpenFileForRead:
            return "FailedToOpenFileForRead";
        case ErrorCode::FailedToOpenFileForWrite:
            return "FailedToOpenFileForWrite";
        case ErrorCode::FailedToLaunchProcess:
            return "FailedToLaunchProcess";
        case ErrorCode::Last:
            return "Last";
        default:
            return "Last";
        }
    }

    ErrorCode FromString(const std::string &errorCode)
    {
        if ("OK" == errorCode)
        {
            return ErrorCode::OK;
        }
        else if("OutOfSpace" == errorCode)
        {
            return ErrorCode::OutOfSpace;
        }
        else if("ConnectionFailure" == errorCode)
        {
            return ErrorCode::ConnectionFailure;
        }
        else if("InvalidJson" == errorCode)
        {
            return ErrorCode::InvalidJson;
        }
        else if("DatabaseFailure" == errorCode)
        {
            return ErrorCode::DatabaseFailure;
        }
        else if("HttpError" == errorCode)
        {
            return ErrorCode::HttpError;
        }
        else if("FileSystemError" == errorCode)
        {
            return ErrorCode::FileSystemError;
        }
        else if("FailedToEncrypt" == errorCode)
        {
            return ErrorCode::FailedToEncrypt;
        }
        else if("FailedToDecrypt" == errorCode)
        {
            return ErrorCode::FailedToDecrypt;
        }
        else if("EndOfFile" == errorCode)
        {
            return ErrorCode::EndOfFile;
        }
        else if("FailedToOpenFileForRead" == errorCode)
        {
            return ErrorCode::FailedToOpenFileForRead;
        }
        else if("FailedToOpenFileForWrite" == errorCode)
        {
            return ErrorCode::FailedToOpenFileForWrite;
        }
        else if("FailedToLaunchProcess" == errorCode)
        {
            return ErrorCode::FailedToLaunchProcess;
        }
        else if("Last" == errorCode)
        {
            return ErrorCode::Last;
        }

        return ErrorCode::Last;
    }
}
Alexandru Irimiea
la source
1
Voici un générateur en ligne: th-thielemann.de/tools/cpp-enum-to-string.html
Th. Thielemann
2

Ajoutant encore plus de simplicité d'utilisation à la réponse fantastique de Jasper Bekkers :

Configurer une fois:

#define MAKE_ENUM(VAR) VAR,
#define MAKE_STRINGS(VAR) #VAR,
#define MAKE_ENUM_AND_STRINGS(source, enumName, enumStringName) \
    enum enumName { \
    source(MAKE_ENUM) \
    };\
const char* const enumStringName[] = { \
    source(MAKE_STRINGS) \
    };

Ensuite, pour l'utilisation:

#define SOME_ENUM(DO) \
    DO(Foo) \
    DO(Bar) \
    DO(Baz)
...
MAKE_ENUM_AND_STRINGS(SOME_ENUM, someEnum, someEnumNames)
François Bertrand
la source
2

Vous pouvez utiliser une bibliothèque de réflexion, comme Ponder . Vous enregistrez les énumérations, puis vous pouvez les convertir d'avant en arrière avec l'API.

enum class MyEnum
{
    Zero = 0,
    One  = 1,
    Two  = 2
};

ponder::Enum::declare<MyEnum>()
    .value("Zero", MyEnum::Zero)
    .value("One",  MyEnum::One)
    .value("Two",  MyEnum::Two);

ponder::EnumObject zero(MyEnum::Zero);

zero.name(); // -> "Zero"
pseudo
la source
1

Un problème avec la réponse 0 est que les valeurs binaires d'énumération ne commencent pas nécessairement à 0 et ne sont pas nécessairement contiguës.

Lorsque j'en ai besoin, je:

  • tirer la définition enum dans ma source
  • modifiez-le pour obtenir uniquement les noms
  • faites une macro pour changer le nom de la clause case dans la question, bien que typiquement sur une seule ligne: case foo: return "foo";
  • ajoutez le commutateur, la syntaxe par défaut et une autre syntaxe pour la rendre légale
mpez0
la source
1

Le script ruby ​​suivant tente d'analyser les en-têtes et construit les sources requises à côté des en-têtes d'origine.

#! /usr/bin/env ruby

# Let's "parse" the headers
# Note that using a regular expression is rather fragile
# and may break on some inputs

GLOBS = [
  "toto/*.h",
  "tutu/*.h",
  "tutu/*.hxx"
]

enums = {}
GLOBS.each { |glob|
  Dir[glob].each { |header|
    enums[header] = File.open(header, 'rb') { |f|
      f.read
    }.scan(/enum\s+(\w+)\s+\{\s*([^}]+?)\s*\}/m).collect { |enum_name, enum_key_and_values|
      [
        enum_name, enum_key_and_values.split(/\s*,\s*/).collect { |enum_key_and_value|
          enum_key_and_value.split(/\s*=\s*/).first
        }
      ]
    }
  }
}


# Now we build a .h and .cpp alongside the parsed headers
# using the template engine provided with ruby
require 'erb'

template_h = ERB.new <<-EOS
#ifndef <%= enum_name %>_to_string_h_
#define <%= enum_name %>_to_string_h_ 1

#include "<%= header %>"
char* enum_to_string(<%= enum_name %> e);

#endif
EOS

template_cpp = ERB.new <<-EOS
#include "<%= enum_name %>_to_string.h"

char* enum_to_string(<%= enum_name %> e)
{
  switch (e)
  {<% enum_keys.each do |enum_key| %>
    case <%= enum_key %>: return "<%= enum_key %>";<% end %>
    default: return "INVALID <%= enum_name %> VALUE";
  }
}
EOS

enums.each { |header, enum_name_and_keys|
  enum_name_and_keys.each { |enum_name, enum_keys|
    File.open("#{File.dirname(header)}/#{enum_name}_to_string.h", 'wb') { |built_h|
      built_h.write(template_h.result(binding))
    }

    File.open("#{File.dirname(header)}/#{enum_name}_to_string.cpp", 'wb') { |built_cpp|
      built_cpp.write(template_cpp.result(binding))
    }
  }
}

L'utilisation d'expressions régulières rend cet "analyseur" assez fragile, il peut ne pas être capable de gérer vos en-têtes spécifiques avec élégance.

Supposons que vous ayez un en-tête toto / ah, contenant des définitions pour les enums MyEnum et MyEnum2. Le script construira:

toto/MyEnum_to_string.h
toto/MyEnum_to_string.cpp
toto/MyEnum2_to_string.h
toto/MyEnum2_to_string.cpp

Des solutions plus robustes seraient:

  • Construisez toutes les sources définissant les énumérations et leurs opérations à partir d'une autre source. Cela signifie que vous définissez vos énumérations dans un fichier XML / YML / n'importe quel fichier qui est beaucoup plus facile à analyser que C / C ++.
  • Utilisez un vrai compilateur tel que suggéré par Avdi.
  • Utilisez des macros de préprocesseur avec ou sans modèles.
bltxd
la source
0

C'est à peu près la seule façon de le faire (un tableau de chaînes pourrait également fonctionner).

Le problème est qu'une fois qu'un programme C est compilé, la valeur binaire de l'énumération est tout ce qui est utilisé, et le nom a disparu.

James Curran
la source
0

Voici un programme CLI que j'ai écrit pour convertir facilement les énumérations en chaînes. Il est facile à utiliser et prend environ 5 secondes pour le faire (y compris le temps de cd dans le répertoire contenant le programme, puis exécutez-le, en lui passant le fichier contenant l'énumération).

Téléchargez ici: http://www.mediafire.com/?nttignoozzz

Sujet de discussion à ce sujet ici: http://cboard.cprogramming.com/projects-job-recruitment/127488-free-program-im-sharing-convertenumtostrings.html

Exécutez le programme avec l'argument "--help" pour obtenir une description de son utilisation.

Programmer_P
la source
Pourriez-vous s'il vous plaît mettre cela sur un référentiel quelque part (github, google code ou bitbucket) et publier le lien ici, au lieu de mediafire? J'aiderais les gens qui veulent le comprendre :)
Edu Felipe
0

Il n'y a pas si longtemps, j'ai fait une astuce pour avoir les énumérations correctement affichées dans QComboBox et pour avoir la définition des représentations enum et chaîne comme une seule instruction

#pragma once
#include <boost/unordered_map.hpp>

namespace enumeration
{

   struct enumerator_base : boost::noncopyable
   {
      typedef
         boost::unordered_map<int, std::wstring>
         kv_storage_t;
      typedef
         kv_storage_t::value_type
         kv_type;
      kv_storage_t const & kv() const
      {
         return storage_;
      }

      LPCWSTR name(int i) const
      {
         kv_storage_t::const_iterator it = storage_.find(i);
         if(it != storage_.end())
            return it->second.c_str();
         return L"empty";
      }

   protected:
      kv_storage_t storage_;
   };

   template<class T>
   struct enumerator;

   template<class D>
   struct enum_singleton : enumerator_base
   {
      static enumerator_base const & instance()
      {
         static D inst;
         return inst;
      }
   };
}

#define QENUM_ENTRY(K, V, N)  K, N storage_.insert(std::make_pair((int)K, V));

#define QBEGIN_ENUM(NAME, C)   \
enum NAME                     \
{                             \
   C                          \
}                             \
};                            \
}                             \

#define QEND_ENUM(NAME) \
};                     \
namespace enumeration  \
{                      \
template<>             \
struct enumerator<NAME>\
   : enum_singleton< enumerator<NAME> >\
{                      \
   enumerator()        \
   {

//usage
/*
QBEGIN_ENUM(test_t,
   QENUM_ENTRY(test_entry_1, L"number uno",
   QENUM_ENTRY(test_entry_2, L"number dos",
   QENUM_ENTRY(test_entry_3, L"number tres",
QEND_ENUM(test_t)))))
*/

Vous pouvez maintenant enumeration::enum_singleton<your_enum>::instance()convertir les énumérations en chaînes. Si vous remplacez kv_storage_tparboost::bimap , vous pourrez également effectuer une conversion en amont. Une classe de base commune pour le convertisseur a été introduite pour le stocker dans un objet Qt, car les objets Qt ne pouvaient pas être des modèles

Apparition précédente

Kassak
la source
0

En variante, utilisez simple lib> http://codeproject.com/Articles/42035/Enum-to-String-and-Vice-Versa-in-C

Dans le code

#include <EnumString.h>

enum FORM {
    F_NONE = 0,
    F_BOX,
    F_CUBE,
    F_SPHERE,
};

ajouter des lignes

Begin_Enum_String( FORM )
{
    Enum_String( F_NONE );
    Enum_String( F_BOX );
    Enum_String( F_CUBE );
    Enum_String( F_SPHERE );
}
End_Enum_String;

Fonctionne très bien, si les valeurs enum ne sont pas dupliquées .

Exemple d'utilisation

enum FORM f = ...
const std::string& str = EnumString< FORM >::From( f );

et vice versa

assert( EnumString< FORM >::To( f, str ) );
Andrey Syrokomskiy
la source
0

Voici une tentative pour obtenir automatiquement les opérateurs de flux << et >> sur enum avec une commande de macro d'une seule ligne ...

Définitions:

#include <string>
#include <iostream>
#include <stdexcept>
#include <algorithm>
#include <iterator>
#include <sstream>
#include <vector>

#define MAKE_STRING(str, ...) #str, MAKE_STRING1_(__VA_ARGS__)
#define MAKE_STRING1_(str, ...) #str, MAKE_STRING2_(__VA_ARGS__)
#define MAKE_STRING2_(str, ...) #str, MAKE_STRING3_(__VA_ARGS__)
#define MAKE_STRING3_(str, ...) #str, MAKE_STRING4_(__VA_ARGS__)
#define MAKE_STRING4_(str, ...) #str, MAKE_STRING5_(__VA_ARGS__)
#define MAKE_STRING5_(str, ...) #str, MAKE_STRING6_(__VA_ARGS__)
#define MAKE_STRING6_(str, ...) #str, MAKE_STRING7_(__VA_ARGS__)
#define MAKE_STRING7_(str, ...) #str, MAKE_STRING8_(__VA_ARGS__)
#define MAKE_STRING8_(str, ...) #str, MAKE_STRING9_(__VA_ARGS__)
#define MAKE_STRING9_(str, ...) #str, MAKE_STRING10_(__VA_ARGS__)
#define MAKE_STRING10_(str) #str

#define MAKE_ENUM(name, ...) MAKE_ENUM_(, name, __VA_ARGS__)
#define MAKE_CLASS_ENUM(name, ...) MAKE_ENUM_(friend, name, __VA_ARGS__)

#define MAKE_ENUM_(attribute, name, ...) name { __VA_ARGS__ }; \
    attribute std::istream& operator>>(std::istream& is, name& e) { \
        const char* name##Str[] = { MAKE_STRING(__VA_ARGS__) }; \
        std::string str; \
        std::istream& r = is >> str; \
        const size_t len = sizeof(name##Str)/sizeof(name##Str[0]); \
        const std::vector<std::string> enumStr(name##Str, name##Str + len); \
        const std::vector<std::string>::const_iterator it = std::find(enumStr.begin(), enumStr.end(), str); \
        if (it != enumStr.end())\
            e = name(it - enumStr.begin()); \
        else \
            throw std::runtime_error("Value \"" + str + "\" is not part of enum "#name); \
        return r; \
    }; \
    attribute std::ostream& operator<<(std::ostream& os, const name& e) { \
        const char* name##Str[] = { MAKE_STRING(__VA_ARGS__) }; \
        return (os << name##Str[e]); \
    }

Usage:

// Declare global enum
enum MAKE_ENUM(Test3, Item13, Item23, Item33, Itdsdgem43);

class Essai {
public:
    // Declare enum inside class
    enum MAKE_CLASS_ENUM(Test, Item1, Item2, Item3, Itdsdgem4);

};

int main() {
    std::cout << Essai::Item1 << std::endl;

    Essai::Test ddd = Essai::Item1;
    std::cout << ddd << std::endl;

    std::istringstream strm("Item2");
    strm >> ddd;

    std::cout << (int) ddd << std::endl;
    std::cout << ddd << std::endl;
}

Je ne suis pas sûr des limites de ce système ... les commentaires sont les bienvenus!

OlivierB
la source
0
#include <iostream>
#include <map>
#define IDMAP(x) (x,#x)

std::map<int , std::string> enToStr;
class mapEnumtoString
{
public:
    mapEnumtoString(){  }
    mapEnumtoString& operator()(int i,std::string str)
    {
        enToStr[i] = str;
        return *this;
    }
public:
   std::string operator [] (int i)
    {
        return enToStr[i];
    }

};
mapEnumtoString k;
mapEnumtoString& init()
{
    return k;
}

int main()
{

init()
    IDMAP(1)
    IDMAP(2)
    IDMAP(3)
    IDMAP(4)
    IDMAP(5);
std::cout<<enToStr[1];
std::cout<<enToStr[2];
std::cout<<enToStr[3];
std::cout<<enToStr[4];
std::cout<<enToStr[5];
}
user3510054
la source
2
Veuillez expliquer pourquoi c'est la réponse.
Alok