Gibt es eine einfache Möglichkeit, die C ++ - Aufzählung in einen String umzuwandeln?


123

Angenommen, wir haben einige benannte Aufzählungen:

enum MyEnum {
      FOO,
      BAR = 0x50
};

Was ich gegoogelt habe, ist ein Skript (jede Sprache), das alle Header in meinem Projekt scannt und einen Header mit einer Funktion pro Aufzählung generiert.

char* enum_to_string(MyEnum t);

Und eine Implementierung mit so etwas:

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

Das Gotcha besteht wirklich aus typisierten Aufzählungen und unbenannten Aufzählungen im C-Stil. Weiß jemand etwas dafür?

BEARBEITEN: Die Lösung sollte meine Quelle mit Ausnahme der generierten Funktionen nicht ändern. Die Aufzählungen befinden sich in einer API, daher ist die Verwendung der bisher vorgeschlagenen Lösungen keine Option.


Die Antwort zur makrobasierten Factory wurde in stackoverflow.com/questions/147267/… verschoben. Nachdem die Frage aktualisiert wurde, ist sie hier nicht mehr relevant.
Suma

Antworten:


48

Vielleicht möchten Sie GCCXML ausprobieren .

Wenn Sie GCCXML in Ihrem Beispielcode ausführen, erhalten Sie Folgendes:

<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>

Sie können jede Sprache verwenden, die Sie bevorzugen, um die Tags Enumeration und EnumValue herauszuziehen und den gewünschten Code zu generieren.


Ausgezeichnet! Arbeitete als Zauber mit einem einfachen Python-Skript. Vielen Dank.
Edu Felipe

6
+1, GCCXML sieht sehr gut aus! (Obwohl ich fast -1ed habe, als ich dies anfangs als Vorschlag missverstanden habe, die obige ausführliche XML-Syntax zu verwenden, um Ihre Aufzählung zu codieren - eine Lösung, die nach
Überentwicklung

1
Gibt es eine Änderung, die Sie im Python-Skript veröffentlichen können?
Phillipwei

74

X-Makros sind die beste Lösung. Beispiel:

#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)

Normalerweise bevorzuge ich jedoch die folgende Methode, damit die Zeichenfolge etwas angepasst werden kann.

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

X(Red, "red")
X(Green, "green")
// etc.

11
geschickt, obwohl ich die zusätzliche Datei nicht mag
Ronny Brendel

2
Stellen Sie einfach sicher, dass Ihr Erstellungsprozess #pragma nicht (einmal) vor jeder Include-Datei voranstellt ...
xtofl

24
Ich bin mir nicht sicher über die "beste" Lösung!
Leichtigkeitsrennen im Orbit

2
Diese Lösung ist jeder Switch-Case- oder Array-basierten Lösung weit überlegen, da die Namen nicht dupliziert werden und die Aufzählung leicht geändert werden kann.
Julien Guertault

2
@ ikku100 Sie sind falsch über #define X(a, b) #b. Dies ist nur erforderlich, wenn die Definition so aussieht X(Red, red)und nicht die in der Antwort gezeigte DefinitionX(Red, "red")
lernen Sie den

43

@hydroo: Ohne die zusätzliche Datei:

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

Ich liebe diese Lösung. Es wäre jedoch klarer, wenn SOME_UNION und MAKE_UNION SOME_ENUM und MAKE_ENUM heißen würden.
Bruno Martinez

Dies ist eine großartige Lösung. Ich habe den wartbarsten C ++ - Ressourcenmanager, mit dem ich mich jemals befasst habe.
DCurro

Ich muss mich bei Ihnen für diese einfache Lösung bedanken :-) - Ich habe sie jedoch ein wenig modifiziert, MetaSyntacticVariableNames[]um Teil einer Klassendeklaration zu sein, indem static const char* getNameByEnum(MetaSyntacticVariable e) { /*code to return the static string*/ }
ich

Fantastische Antwort! Ich habe es weiter vereinfacht, indem ich MAKE_ENUM und MAKE_STRINGS in einem einzigen Makro gruppiert habe, um den gesamten Prozess noch einfacher zu gestalten. Ich habe in diesem Thread eine Antwort mit diesem Code hinzugefügt, wenn jemand interessiert ist.
Francois Bertrand

35

Ich neige dazu, ein C-Array mit den Namen in derselben Reihenfolge und Position wie die Enum-Werte zu erstellen.

z.B.

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

Dann können Sie das Array an Stellen verwenden, an denen Sie einen für Menschen lesbaren Wert wünschen, z

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

Sie können ein wenig mit dem Stringisierungsoperator experimentieren (siehe # in Ihrer Präprozessorreferenz), der unter bestimmten Umständen das tut, was Sie wollen - z.

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

druckt "rot" auf stdout. Leider funktioniert es für eine Variable nicht (da der Variablenname ausgedruckt wird)


Die letzte Einschränkung (funktioniert bei einer Variablen nicht) ist ein großer Nachteil, aber trotzdem +1.
Chappjc

3
Funktioniert nur, wenn Sie keine speziellen numerischen Werte für Aufzählungseinträge festlegen.
Kyb

11

Ich habe ein unglaublich einfach zu verwendendes Makro, das dies völlig trocken macht. Es beinhaltet verschiedene Makros und einige einfache Parsing-Magie. Hier geht:

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

Um dies in Ihrem Code zu verwenden, gehen Sie einfach wie folgt vor:

AWESOME_MAKE_ENUM(Animal,
    DOG,
    CAT,
    HORSE
);

1
Gute Idee mit einer stark typisierten Aufzählung (Aufzählungsklasse). Hier ist eine Demo: cpp.sh/4ife
chappjc

Funktioniert dies mit extern definierten Aufzählungen / Symbolen? Zum Beispiel OS-definierte oder bibliotheksdefinierte Symbole mit Lücken in der Nummerierung?
Jason Harrison

Sehr schön, wird aber nicht kompiliert, wenn es in eine Klasse eingefügt wird (ich konnte nicht herausfinden warum).
AlwaysLearning

Ich konnte dies nicht in VS2015 kompilieren. Ich erhalte eine Warnung und einen Fehler: Warnung: mehrzeiliger Kommentar [-Wcomment] #define MAKE_ENUM (Name, ...) Aufzählungsklassenname { VA_ARGS , __COUNT} Fehler: Streuner '#' im Programm std *: Zeichenfolge enumName = #name
Craig.Feied

8

QT kann das von (dank des Meta-Objekt-Compilers) ziehen:

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;

}

In Qt hat jede Klasse mit dem Makro Q_OBJECT automatisch ein statisches Element "staticMetaObject" vom Typ QMetaObject. Sie können dann alle möglichen coolen Dinge wie Eigenschaften, Signale, Slots und Aufzählungen finden.

Quelle


7

Dies kann in C ++ 11 erfolgen

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

1
Dies beantwortet nicht die Frage des OP: Er suchte nach einer Möglichkeit, automatisch eine Funktion zu generieren , um den Namen eines Enum-Mitglieds als Zeichenfolge zurückzugeben.
Spooky

7

Ich habe dieses Rad heute neu erfunden und dachte, ich würde es teilen.

Diese Implementierung ist nicht erforderlich Änderungen an den Code, der die Konstanten definiert, die Aufzählungen oder sein kann , #defines oder irgendetwas anderes , dass zufällt auf eine ganze Zahl - in meinem Fall hatte ich Symbole in Bezug auf den anderen Symbolen definiert. Es funktioniert auch gut mit spärlichen Werten. Es sind sogar mehrere Namen für denselben Wert zulässig, wobei immer der erste zurückgegeben wird. Der einzige Nachteil ist, dass Sie eine Tabelle mit den Konstanten erstellen müssen, die möglicherweise veraltet sind, wenn beispielsweise neue hinzugefügt werden.

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

Ein Beispiel, wie Sie es verwenden würden:

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

Die IdToNameFunktion beruht aufstd::lower_bound schnellen Suchvorgängen, bei denen die Tabelle sortiert werden muss. Wenn die ersten beiden Einträge in der Tabelle nicht in der richtigen Reihenfolge sind, wird sie von der Funktion automatisch sortiert.

Bearbeiten: Ein Kommentar ließ mich über eine andere Möglichkeit nachdenken, dasselbe Prinzip anzuwenden. Ein Makro vereinfacht die Generierung einer großen switchAnweisung.

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

Gute Lösung. Aber für mich würde ich es vorziehen, switch and caseda es einfach und leicht zu verstehen ist.
Deqing

6
#define stringify( name ) # name

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

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

Weitere Diskussion zu dieser Methode

Tricks der Präprozessor-Direktive für Neulinge


4
Tatsächlich ist dies ziemlich nutzlos, da sich die Stringify-Methode zur Kompilierungszeit befindet und ziemlich wörtlich ist. Wenn Sie sagen, dass der betreffende Aufzählungstyp in einer Variablen enthalten ist, erhalten Sie beim Versuch, die Variable zu stringifizieren, nur den Variablennamen und nicht den Namen des Aufzählungstyps.
srcspider

5

Interessant zu sehen, wie viele Möglichkeiten es gibt. Hier ist eine, die ich vor langer Zeit benutzt habe:

in der Datei 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(){};
};

in main.cpp

#include "myenummap.h"

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

Es ist nicht const, aber es ist bequem.

Hier ist eine andere Möglichkeit, die C ++ 11-Funktionen verwendet. Dies ist const, erbt keinen STL-Container und ist etwas aufgeräumter:

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

1
Es ist vollkommen legal. Das mache ich die ganze Zeit.
Jonathan Graehl

Gute Lösung. Dies ist c ++, daher ist die Verwendung von stl map in Ordnung.
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();

Verwendung:

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

1
Ich mag Ihre API, aber leider erstellt Ihr SmartEnum keinen Aufzählungstyp. Das kannst du nicht machen MyEnum x = MyEnum::TWO;. Ich habe meine Bearbeitung Ihrer Klasse veröffentlicht, um dies zu unterstützen.
Mark Lakata

4

Sumas Makrolösung ist nett. Sie müssen jedoch nicht zwei verschiedene Makros haben. C ++ enthält gerne zweimal einen Header. Lassen Sie einfach die Include-Wache weg.

Sie hätten also eine foobar.h, die nur definiert

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

und Sie würden es so einschließen:

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

enumfactory.h macht 2 #include ENUMFACTORY_ARGUMENTs. In der ersten Runde wird ENUM wie bei Suma erweitert DECLARE_ENUM. in der zweiten Runde funktioniert ENUM wie DEFINE_ENUM.

Sie können enumfactory.h auch mehrmals einschließen, solange Sie verschiedene # Definitionen für ENUMFACTORY_ARGUMENT übergeben


anscheinend hat suma die antwort hierher verschoben . Möglicherweise möchten Sie den Link in Ihre Antwort aufnehmen. Ich fand den Kommentar nur zufällig und ohne Sumas-Antwort ist dieser ziemlich sinnlos
idclev 463035818

3

Beachten Sie, dass Ihre Konvertierungsfunktion idealerweise ein const char * zurückgeben sollte.

Wenn Sie es sich leisten können, Ihre Aufzählungen in ihre separaten Header-Dateien zu stellen, könnten Sie vielleicht so etwas mit Makros machen (oh, das wird hässlich):

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

Wo enum_def.h hat:

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

Und enum_conv.h hat:

#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"; } }

Und schließlich hat colour.h:

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

Und Sie können die Konvertierungsfunktion verwenden als:

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

Dies ist hässlich, aber es ist die einzige Möglichkeit (auf Präprozessorebene), mit der Sie Ihre Aufzählung nur an einer einzigen Stelle in Ihrem Code definieren können. Ihr Code ist daher nicht fehleranfällig aufgrund von Änderungen an der Aufzählung. Ihre Aufzählungsdefinition und die Konvertierungsfunktion sind immer synchron. Ich wiederhole jedoch, das ist hässlich :)


3

Eine andere Antwort: In einigen Kontexten ist es sinnvoll, Ihre Aufzählung in einem Nicht-Code-Format wie einer CSV-, YAML- oder XML-Datei zu definieren und dann sowohl den C ++ - Aufzählungscode als auch den To-String-Code aus der Definition zu generieren. Dieser Ansatz kann in Ihrer Anwendung praktisch sein oder auch nicht, ist jedoch zu beachten.


3

Dies ist eine Änderung der Antwort @ user3360260. Es hat die folgenden neuen Funktionen

  • MyEnum fromString(const string&) Unterstützung
  • Kompiliert mit VisualStudio 2012
  • Die Aufzählung ist ein tatsächlicher POD-Typ (nicht nur const-Deklarationen), sodass Sie sie einer Variablen zuweisen können.
  • C ++ "Range" -Funktion (in Form eines Vektors) hinzugefügt, um "foreach" -Iteration über Enum zu ermöglichen

Verwendung:

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

Hier ist der 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; } \
};         

Beachten Sie, dass die Konvertierung in String eine schnelle Suche ist, während die Konvertierung in String eine langsame lineare Suche ist. Aber Strings sind sowieso so teuer (und die zugehörige Datei-E / A), dass ich nicht das Bedürfnis hatte, eine Bimap zu optimieren oder zu verwenden.


Sie und user3360260 haben eine gute Lösung. Warum nicht stattdessen eine Multimap?
Vincent

3

Hier eine One-File-Lösung (basierend auf der eleganten Antwort von @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;
}

2

Ich mache das mit separaten Enum-Wrapper-Klassen nebeneinander, die mit Makros generiert werden. Es gibt mehrere Vorteile:

  • Kann sie für nicht definierte Aufzählungen generieren (z. B. OS-Plattform-Header-Aufzählungen)
  • Kann die Bereichsprüfung in die Wrapper-Klasse integrieren
  • Kann mit Bitfeld-Aufzählungen "intelligenter" formatieren

Der Nachteil ist natürlich, dass ich die Enum-Werte in den Formatierungsklassen duplizieren muss und kein Skript habe, um sie zu generieren. Davon abgesehen scheint es jedoch ziemlich gut zu funktionieren.

Hier ist ein Beispiel für eine Aufzählung aus meiner Codebasis ohne den gesamten Framework-Code, der die Makros und Vorlagen implementiert, aber Sie können sich vorstellen:

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;

Die Idee ist dann, anstatt EHelpLocation zu verwenden, SEHelpLocation zu verwenden. Alles funktioniert gleich, aber Sie erhalten eine Bereichsprüfung und eine 'Format ()' - Methode für die Enum-Variable selbst. Wenn Sie einen eigenständigen Wert formatieren müssen, können Sie CEnumFormatter_EHelpLocation :: FormatEnum (...) verwenden.

Hoffe das ist hilfreich. Mir ist klar, dass dies auch nicht die ursprüngliche Frage nach einem Skript zum Generieren der anderen Klasse anspricht, aber ich hoffe, dass die Struktur jemandem hilft, der versucht, dasselbe Problem zu lösen oder ein solches Skript zu schreiben.


2

Es ist unveröffentlichte Software, aber es scheint, dass BOOST_ENUM von Frank Laub genau das Richtige für Sie sein könnte. Der Teil, den ich daran mag, ist, dass Sie eine Aufzählung im Rahmen einer Klasse definieren können, die die meisten makrobasierten Aufzählungen normalerweise nicht zulassen. Es befindet sich im Boost-Tresor unter: http://www.boostpro.com/vault/index.php?action=downloadfile&filename=enum_rev4.6.zip&directory=& Es hat seit 2006 keine Entwicklung mehr gesehen, daher habe ich keine wissen, wie gut es mit den neuen Boost-Versionen kompiliert. Suchen Sie unter libs / test nach einem Verwendungsbeispiel.


2

Dies war meine Lösung mit 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());                        \
    }

Um eine Aufzählung zu erstellen, deklarieren Sie:

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

Für Umbauten:

SERVICE_RELOAD serviceReloadEnum = strToEnum<SERVICE_RELOAD>("reload_log");
QString serviceReloadStr = enumToStr(reload_log);

2

Ich möchte dies posten, falls jemand es nützlich findet.

In meinem Fall muss ich einfach eine einzelne C ++ 11-Enumeration aus einer einzelnen Datei generieren ToString()und FromString()funktionieren .hpp.

Ich habe ein Python-Skript geschrieben, das die Header-Datei mit den Enum-Elementen analysiert und die Funktionen in einer neuen .cppDatei generiert .

Sie können dieses Skript in CMakeLists.txt mit execute_process oder als Pre-Build-Ereignis in Visual Studio hinzufügen . Die .cppDatei wird automatisch generiert, ohne dass sie jedes Mal manuell aktualisiert werden muss, wenn ein neues Aufzählungselement hinzugefügt wird.

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)

Beispiel:

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

Lauf python generate_enum_strings.py ErrorCode.hpp

Ergebnis:

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


2

Die fantastische Antwort von Jasper Bekkers wird noch einfacher :

Einmal einrichten:

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

Dann zur Verwendung:

#define SOME_ENUM(DO) \
    DO(Foo) \
    DO(Bar) \
    DO(Baz)
...
MAKE_ENUM_AND_STRINGS(SOME_ENUM, someEnum, someEnumNames)

2

Sie könnten eine Reflexionsbibliothek wie Ponder verwenden . Sie registrieren die Aufzählungen und können sie dann mit der API hin und her konvertieren.

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"

1

Ein Problem mit Antwort 0 besteht darin, dass die Enum-Binärwerte nicht unbedingt bei 0 beginnen und nicht unbedingt zusammenhängend sind.

Wenn ich das brauche, mache ich normalerweise:

  • Ziehen Sie die Aufzählungsdefinition in meine Quelle
  • Bearbeiten Sie es, um nur die Namen zu erhalten
  • Führen Sie ein Makro aus, um den Namen in die case-Klausel in der Frage zu ändern, normalerweise jedoch in einer Zeile: case foo: return "foo";
  • Fügen Sie den Schalter, die Standardeinstellung und andere Syntax hinzu, um sie legal zu machen

1

Das folgende Ruby-Skript versucht, die Header zu analysieren, und erstellt die erforderlichen Quellen neben den ursprünglichen Headern.

#! /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))
    }
  }
}

Die Verwendung regulärer Ausdrücke macht diesen "Parser" sehr zerbrechlich. Möglicherweise kann er Ihre spezifischen Header nicht ordnungsgemäß verarbeiten.

Angenommen, Sie haben einen Header toto / ah, der Definitionen für die Aufzählungen MyEnum und MyEnum2 enthält. Das Skript wird Folgendes erstellen:

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

Robustere Lösungen wären:

  • Erstellen Sie alle Quellen, die Aufzählungen und ihre Operationen definieren, aus einer anderen Quelle. Dies bedeutet, dass Sie Ihre Aufzählungen in einer XML / YML / was auch immer-Datei definieren, die viel einfacher zu analysieren ist als C / C ++.
  • Verwenden Sie einen echten Compiler, wie von Avdi vorgeschlagen.
  • Verwenden Sie Präprozessor-Makros mit oder ohne Vorlagen.

0

Dies ist so ziemlich die einzige Möglichkeit (ein Array von Zeichenfolgen könnte auch funktionieren).

Das Problem ist, dass nach dem Kompilieren eines C-Programms nur der Binärwert der Aufzählung verwendet wird und der Name weg ist.


0

Hier ist ein CLI-Programm, das ich geschrieben habe, um Enums einfach in Strings zu konvertieren. Es ist einfach zu bedienen und benötigt ca. 5 Sekunden, um es fertig zu stellen (einschließlich der Zeit, um in das Verzeichnis mit dem Programm zu cd, dann führen Sie es aus und übergeben Sie ihm die Datei mit der Aufzählung).

Hier herunterladen: http://www.mediafire.com/?nttignoozzz

Diskussionsthema dazu hier: http://cboard.cprogramming.com/projects-job-recruitment/127488-free-program-im-sharing-convertenumtostrings.html

Führen Sie das Programm mit dem Argument "--help" aus, um eine Beschreibung der Verwendung zu erhalten.


Könnten Sie dies bitte irgendwo in ein Repository stellen (Github, Google Code oder Bitbucket) und den Link hier anstelle von Mediafire posten? Ich würde Leuten helfen, die es verstehen wollen :)
Edu Felipe

0

Vor nicht allzu langer Zeit habe ich einen Trick gemacht, um Enums korrekt in QComboBox anzuzeigen und die Definition von Enum- und String-Darstellungen als eine Anweisung zu haben

#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)))))
*/

Jetzt können Sie enumeration::enum_singleton<your_enum>::instance()Aufzählungen in Zeichenfolgen konvertieren. Wenn Sie ersetzen kv_storage_tmit boost::bimap, werden Sie auch rückwärts Umwandlung der Lage zu tun. Die allgemeine Basisklasse für den Konverter wurde eingeführt, um sie im Qt-Objekt zu speichern, da Qt-Objekte keine Vorlagen sein konnten

Vorheriger Auftritt


0

Verwenden Sie als Variante die einfache lib> http://codeproject.com/Articles/42035/Enum-to-String-and-Vice-Versa-in-C

Im Code

#include <EnumString.h>

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

Zeilen hinzufügen

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

Funktionieren Sie einwandfrei , wenn die Werte in enum nicht veröffentlicht sind .

Anwendungsbeispiel

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

und umgekehrt

assert( EnumString< FORM >::To( f, str ) );

0

Hier ist ein Versuch, << und >> Stream-Operatoren automatisch mit einem einzeiligen Makrobefehl auf enum zu bringen ...

Definitionen:

#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]); \
    }

Verwendung:

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

Ich bin mir nicht sicher über die Einschränkungen dieses Schemas ... Kommentare sind willkommen!


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

2
Bitte erklären Sie, warum dies die Antwort ist.
Alok
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.