it-swarm.com.de

Gibt es eine einfache Möglichkeit, C++ - Enummen in Zeichenfolgen zu konvertieren?

Nehmen wir an, wir haben einige benannte enums:

enum MyEnum {
      FOO,
      BAR = 0x50
};

Was ich gegoogelt habe, ist ein Skript (jede Sprache), das alle Header in meinem Projekt durchsucht 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";
      }
 }

Die gotcha ist wirklich mit typengeprägten Enums und unbenannten C-artigen Enums. Weiß jemand etwas dafür?

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

109
Edu Felipe

Vielleicht möchten Sie GCCXML auschecken.

Wenn Sie GCCXML für Ihren 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 gewünschte Sprache verwenden, um die Tags Enumeration und EnumValue abzurufen und den gewünschten Code zu generieren.

46
Avdi

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)

Ich bevorzuge jedoch in der Regel die folgende Methode, so dass es möglich ist, den String etwas zu verändern.

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

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

@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)
};
41
Jasper Bekkers

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

z.B.

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

dann können Sie das Array an Orten 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önnten ein wenig mit dem String-Operator (siehe # in Ihrer Präprozessor-Referenz) experimentieren, der unter Umständen das tut, was Sie möchten, zB:

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

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

30
gbjbaanb

Ich habe ein unglaublich einfaches Makro, das dies vollständig DRY 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, führen Sie einfach Folgendes aus:

AWESOME_MAKE_ENUM(Animal,
    DOG,
    CAT,
    HORSE
);
9
Debdatta Basu

QT kann das von (dank des Metaobjekt-Compilers) abrufen: link

8
Ronny Brendel

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

Für diese Implementierung sind nicht Änderungen am Code erforderlich, der die Konstanten definiert. Dies können Aufzählungen oder #defines oder alles andere sein, das sich aus einer Ganzzahl ergibt. In meinem Fall hatte ich Symbole in Form anderer Symbole definiert. Es funktioniert auch gut mit spärlichen Werten. Es erlaubt sogar mehrere Namen für denselben Wert und gibt immer den ersten zurück. Der einzige Nachteil ist, dass Sie eine Tabelle der Konstanten erstellen müssen, die veraltet werden kann, wenn zum Beispiel 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 Funktion IdToName ist auf std::lower_bound angewiesen, um eine schnelle Suche durchzuführen. Dazu muss die Tabelle sortiert werden. Wenn die ersten beiden Einträge in der Tabelle nicht in Ordnung sind, wird die Funktion diese automatisch sortieren.

Edit: Ein Kommentar brachte mich auf eine andere Art, dieses Prinzip anzuwenden. Ein Makro vereinfacht die Generierung einer großen switch-Anweisung.

#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;
    }
}
7
Mark Ransom
#define stringify( name ) # name

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

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

Weitere Diskussion zu dieser Methode

Präprozessor-Direktive für Neuankömmlinge

6
Ben

Interessant, um die Anzahl der Möglichkeiten zu sehen. 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 praktisch.

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

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

Verwendungszweck:

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

Dies kann in C++ 11 durchgeführt werden

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

Dies ist eine Änderung der Antwort von @ 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), Sie können ihn also einer Variablen zuweisen.
  • c ++ "Range" -Feature (in Vektorform) hinzugefügt, um "foreach" -Iteration über Enumeration zu ermöglichen

Verwendungszweck:

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 von String eine langsame lineare Suche ist. Aber Strings sind sowieso so teuer (und die zugehörige Datei-IO), dass ich nicht das Bedürfnis hatte, eine Bimap zu optimieren oder zu verwenden.

3
Mark Lakata

Sumas Makrolösung ist Nizza. Sie müssen jedoch nicht zwei verschiedene Makros haben. C++ wird gerne zweimal einen Header einfügen. Lassen Sie den Include-Guard einfach weg.

Sie hätten also eine foobar.h-Definition

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

und du würdest es so hinzufügen:

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

enumfactory.h führt 2 #include ENUMFACTORY_ARGUMENTs aus. In der ersten Runde erweitert es ENUM wie Sumas DECLARE_ENUM; In der zweiten Runde funktioniert ENUM wie DEFINE_ENUM.

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

3
MSalters

Eine andere Antwort: In einigen Kontexten ist es sinnvoll, Ihre Enumeration in einem Nicht-Code-Format zu definieren, wie z. B. einer CSV-, YAML- oder XML-Datei, und dann aus der Definition sowohl den C++ - Enumerationscode als auch den To-String-Code zu generieren. Dieser Ansatz ist in Ihrer Anwendung möglicherweise nicht praktikabel, sollte jedoch beachtet werden.

3
Avdi

Jasper Bekkers 'fantastische Antwort macht die Anwendung 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

Ich mache dies mit separaten Side-by-Side-Enum-Wrapper-Klassen, die mit Makros generiert werden. Es gibt mehrere Vorteile:

  • Kann sie für Aufzählungen generieren, die ich nicht definiere (zB: OS-Plattformheaderummen)
  • Kann die Bereichsüberprüfung in die Wrapper-Klasse integrieren
  • Kann "intelligentere" Formatierungen mit Bitfeld-Enummen durchführen

Der Nachteil ist natürlich, dass ich die Aufzählungswerte in den Formatierungsklassen duplizieren muss, und ich habe kein Skript, um sie zu generieren. Abgesehen davon scheint es aber recht gut zu funktionieren.

Hier ist ein Beispiel für ein Enum aus meiner Codebase, ohne den gesamten Framework-Code, der die Makros und Vorlagen implementiert, aber Sie können die Idee bekommen:

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, verwenden Sie SEHelpLocation. alles funktioniert gleich, aber Sie erhalten eine Bereichsüberprü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 anspricht, um tatsächlich die andere Klasse zu generieren, aber ich hoffe, die Struktur hilft jemandem, der versucht, das gleiche Problem zu lösen oder ein solches Skript zu schreiben.

2
Nick

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

Wenn Sie es sich leisten können, Ihre Enums in separate Header-Dateien zu schreiben, könnten Sie vielleicht mit Makros so etwas tun (oh, das wird hässlich):

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

Wo hat enum_def.h:

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

Das ist hässlich, aber es ist der einzige Weg (auf der Ebene des Präprozessors), mit dem Sie Ihre Enumeration nur an einer einzigen Stelle in Ihrem Code definieren können. Ihr Code ist daher aufgrund von Änderungen der Aufzählung nicht fehleranfällig. Ihre Enumendefinition und die Konvertierungsfunktion sind immer synchron. Ich wiederhole jedoch, das ist hässlich :)

2
Ates Goral

Hier eine One-File-Lösung (basierend auf einer 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
FractalSpace

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

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

Ein Problem bei der Antwort 0 ist, dass die Enumerationswerte nicht notwendigerweise bei 0 beginnen und nicht notwendigerweise zusammenhängend sind.

Wenn ich das brauche, normalerweise:

  • ziehe die enum-Definition in meine Quelle
  • bearbeiten Sie es, um nur die Namen zu erhalten
  • führen Sie ein Makro aus, um den Namen in der Fallklausel in der Frage zu ändern, normalerweise jedoch in einer Zeile: case foo: return "foo";
  • fügen Sie den Schalter, die Standard- und andere Syntax hinzu, um es legal zu machen
1
mpez0

Sie könnten eine Reflexionsbibliothek verwenden, wie Ponder . Sie registrieren das Enum und können es 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
Nick

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

#! /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" ziemlich zerbrechlich, er kann Ihre spezifischen Header möglicherweise nicht elegant behandeln.

Angenommen, Sie haben einen Header toto/a.h, 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, in denen Enums und deren Operationen definiert sind, aus einer anderen Quelle. Dies bedeutet, dass Sie Ihre Enums in einer XML-/YML-Datei definieren, die viel einfacher zu parsen ist als C/C++.
  • Verwenden Sie einen echten Compiler, wie von Avdi vorgeschlagen.
  • Verwenden Sie Präprozessor-Makros mit oder ohne Vorlagen.
1
bltxd

Es ist nicht veröffentlichte Software, aber es scheint, als könnten BOOST_ENUM von Frank Laub die Rechnung erfüllen. Ich mag es an diesem Punkt, dass Sie eine Enumeration innerhalb einer Klasse definieren können, die die meisten Makro-basierten Enummen normalerweise nicht zulassen. Es befindet sich im Boost Vault unter: http://www.boostpro.com/vault/index.php?action=downloadfile&filename=enum_rev4.6.Zip&directory=& .__ Es hat keine Entwicklung gesehen Ich weiß also seit 2006 nicht, wie gut es mit den neuen Boost-Versionen kompiliert werden kann Suchen Sie unter libs/test nach einem Verwendungsbeispiel.

1
Alexis

Vor nicht allzu langer Zeit habe ich einen Trick gemacht, um Enumerationen in QComboBox richtig anzuzeigen und die Definition von Enummen 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() in die Lage, Zeichenfolgen zu konvertieren. Wenn Sie kv_storage_t durch boost::bimap ersetzen, können Sie auch eine Rückwärtskonvertierung durchführen Die allgemeine Basisklasse für den Konverter wurde eingeführt, um ihn im Qt-Objekt zu speichern, da es sich bei Qt-Objekten nicht um Vorlagen handeln kann

Vorheriger Auftritt

0
kassak

Hier ist ein CLI-Programm, das ich geschrieben habe, um Enummen in Strings zu konvertieren. Es ist einfach zu verwenden und dauert etwa 5 Sekunden, um es fertig zu stellen (einschließlich der Zeit, in der cd das Verzeichnis enthält, in dem sich das Programm befindet die Datei, die die Aufzählung enthält).

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 zu erhalten.

0
Programmer_P

Ich bin auf diese Frage gestoßen, als ich nach einer Lösung für mein eigenes Problem suchte, weil ich die "Wörter" der Aufzählung in C++ gedruckt hatte. Ich kam zurück, um eine einfache Lösung bereitzustellen, die die vorgelegte Frage formuliert beantwortet. Alles, was erforderlich ist, ist, die Aufzählungsliste mit einem Vektor zu "spiegeln".

enum class genre { Fiction, NonFiction, Periodical, Biography, Children };

vector<string>genre_tbl { "Fiction", "NonFiction", "Periodical", "Biography", "Children" };

Weil die oben angegebene Aufzählung standardmäßig folgende Vorgänge ausführt:

Fiction = 0
NonFiction = 1
Periodical = 2
Biography = 3
Children = 4

Dies stimmt mit den Vektorpositionen überein, was die Umwandlung von Enum in String ziemlich einfach macht.

string s1 = genre_tbl[int(genre::fiction)];

Für mein Problem habe ich eine benutzerdefinierte Klasse namens Book mit einem Member namens Gen vom Typ Genre erstellt. Das Programm musste das Genre als Word drucken können.

class book {...};
ostream& operator<<(ostream& os, genre g) { return os << genre_tbl[int(g)]; }

book b1;
b1.Gen = genre(0)
cout << b1.Gen;

Für welche "Fiktion" wird in diesem Fall auf den Bildschirm gedruckt.

0
Joe C

Nun, noch eine andere Option. Ein typischer Anwendungsfall ist, wo Sie Konstante für die HTTP-Verben sowie die Verwendung von String-Versionswerten benötigen.

Das Beispiel:

int main () {

  VERB a = VERB::GET;
  VERB b = VERB::GET;
  VERB c = VERB::POST;
  VERB d = VERB::PUT;
  VERB e = VERB::DELETE;


  std::cout << a.toString() << std::endl;

  std::cout << a << std::endl;

  if ( a == VERB::GET ) {
    std::cout << "yes" << std::endl;
  }

  if ( a == b ) {
    std::cout << "yes" << std::endl;
  }

  if ( a != c ) {
    std::cout << "no" << std::endl;
  }

}

Die VERB-Klasse:

// -----------------------------------------------------------
// -----------------------------------------------------------
class VERB {

private:

  // private constants
  enum Verb {GET_=0, POST_, PUT_, DELETE_};

  // private string values
  static const std::string theStrings[];

  // private value
  const Verb value;
  const std::string text;

  // private constructor
  VERB (Verb v) :
  value(v), text (theStrings[v])
  {
    // std::cout << " constructor \n";
  }

public:

  operator const char * ()  const { return text.c_str(); }

  operator const std::string ()  const { return text; }

  const std::string toString () const { return text; }

  bool operator == (const VERB & other) const { return (*this).value == other.value; }

  bool operator != (const VERB & other) const { return ! ( (*this) == other); }

  // ---

  static const VERB GET;
  static const VERB POST;
  static const VERB PUT;
  static const VERB DELETE;

};

const std::string VERB::theStrings[] = {"GET", "POST", "PUT", "DELETE"};

const VERB VERB::GET = VERB ( VERB::Verb::GET_ );
const VERB VERB::POST = VERB ( VERB::Verb::POST_ );
const VERB VERB::PUT = VERB ( VERB::Verb::PUT_ );
const VERB VERB::DELETE = VERB ( VERB::Verb::DELETE_ );
// end of file
0
cibercitizen1

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

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

Verwendungszweck:

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

Nicht sicher über die Einschränkungen dieses Schemas ... Kommentare sind willkommen!

0
OlivierB

Ich möchte dies posten, falls jemand es für nützlich hält. 

In meinem Fall muss ich einfach ToString()- und FromString()-Funktionen für eine einzelne C++ 11-Enumeration aus einer einzigen .hpp-Datei generieren.

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

Sie können dieses Skript in CMakeLists.txt mit execute_process oder als Vorbereitungsereignis in Visual Studio hinzufügen. Die .cpp-Datei wird automatisch generiert, ohne dass sie jedes Mal, wenn ein neues Enumerationselement hinzugefügt wird, manuell aktualisiert werden muss.

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

python generate_enum_strings.py ErrorCode.hpp ausführen

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

Das ist so ziemlich die einzige Möglichkeit, dies zu tun (ein String-Array könnte auch funktionieren).

Das Problem besteht darin, dass nach dem Kompilieren eines C-Programms nur der binäre Wert der Aufzählung verwendet wird und der Name weg ist.

0
James Curran

Diese Frage ist ein Duplikat von

In keiner der Fragen konnte ich jedoch gute Antworten finden.

Nachdem ich mich eingehender mit dem Thema beschäftigt hatte, fand ich zwei großartige Open Source-Lösungen:

wise_enum

  • Standalone-Smart-Enum-Bibliothek für C++ 11/14/17. Es unterstützt alle Standardfunktionen, die Sie von einer Smart-Enum-Klasse in C++ erwarten würden.
  • Einschränkungen: erfordert mindestens C++ 11.

Better Enums

  • Reflective-Kompilierzeitbibliothek mit sauberer Syntax, in einer einzelnen Header-Datei und ohne Abhängigkeiten.
  • Einschränkungen: Basierend auf Makros, können nicht innerhalb einer Klasse verwendet werden.

Hinweis: Ich wiederhole die Empfehlung hier. Diese Frage hat viel Verkehr/Ansichten und erfordert wirklich das Auflisten der obigen Lösungen.

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

Überprüfen Sie diesen Beitrag:

Klassenimplementierung von C++ - Enums

es enthält die Klassenimplementierung von C++ - Enum.

0
ray pixar

Als Variante verwenden Sie einfach 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 gut, wenn Werte in enum nicht überlappend sind.

Verwendungsbeispiel

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

und umgekehrt

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

Die Verwendung von zusammengesetzten ternären Anweisungen kann für Enumerationen mit wenigen Elementen (Einzeiler) etwas elegant sein. Der Ausdruck wächst nur annähernd linear mit der Anzahl der Elemente.

Hier ist ein guter Anwendungsfall:

enum log_level {INFO, WARNING, ERROR};
...
void logger::write(const std::string log, const log_level l) {
    ...
    std::string s = (l == INFO) ? "INFO" : 
                    (l == WARNING) ? "WARNING" : 
                    (l == ERROR) ? "ERROR" : "UNKNOWN";
    ...
}
...

Natürlich ist es nur ein weiterer switch/if-Anweisungsblock, aber es ist eine einzeilige Anweisung. Aus Gründen der Vereinfachung versus Einfachheit trifft es irgendwo in der Mitte auf. Als konstanter Ausdruck kann es auch leicht in eine Inline-Funktion umgewandelt werden.

0
Josh Detwiler