it-swarm.com.de

So ordnen Sie einfach C++ - Enums zu Zeichenfolgen zu

Ich habe eine Reihe von Enummentypen in einigen Bibliotheks-Header-Dateien, die ich verwende, und ich möchte eine Möglichkeit haben, Enumerationswerte in Benutzerzeichenfolgen zu konvertieren - und umgekehrt. 

RTTI macht das nicht für mich, da die 'Benutzerzeichenfolgen' etwas lesbarer sein müssen als die Aufzählungen.

Eine Brute-Force-Lösung wäre eine Reihe von Funktionen wie diese, aber ich finde, das ist ein bisschen zu C-förmig.

enum MyEnum {VAL1, VAL2,VAL3};

String getStringFromEnum(MyEnum e)
{
  switch e
  {
  case VAL1: return "Value 1";
  case VAL2: return "Value 2";
  case VAL1: return "Value 3";
  default: throw Exception("Bad MyEnum");
  }
}

Ich habe das Gefühl, dass es eine elegante Lösung mit Vorlagen gibt, aber ich kann mich noch nicht ganz damit auskennen.

UPDATE: Vielen Dank für Vorschläge - Ich hätte deutlich machen sollen, dass die Aufzählungen in einem Bibliotheksheader eines Drittanbieters definiert sind, sodass ich deren Definition nicht ändern muss.

Mein Gefühl ist jetzt, Vorlagen zu vermeiden und so etwas zu tun:

char * MyGetValue(int v, char *tmp); // implementation is trivial

#define ENUM_MAP(type, strings) char * getStringValue(const type &T) \
 { \
 return MyGetValue((int)T, strings); \
 }

; enum eee {AA,BB,CC}; - exists in library header file 
; enum fff {DD,GG,HH}; 

ENUM_MAP(eee,"AA|BB|CC")
ENUM_MAP(fff,"DD|GG|HH")

// To use...

    eee e;
    fff f;
    std::cout<< getStringValue(e);
    std::cout<< getStringValue(f);
105
Roddy

Wenn Sie möchten, dass die Enumennamen selbst als Zeichenfolgen definiert werden, lesen Sie this post . Andernfalls funktioniert ein std::map<MyEnum, char const*> gut. (Es macht keinen Sinn, Ihre String-Literale nach std :: strings in der Map zu kopieren.)

Für zusätzlichen syntaktischen Zucker schreiben Sie eine map_init-Klasse. Das Ziel ist es zu erlauben

std::map<MyEnum, const char*> MyMap;
map_init(MyMap)
    (eValue1, "A")
    (eValue2, "B")
    (eValue3, "C")
;

Die Funktion template <typename T> map_init(T&) gibt einen map_init_helper<T> zurück. map_init_helper<T> speichert ein T & amp; und definiert das triviale map_init_helper& operator()(typename T::key_type const&, typename T::value_type const&). (Rückgabe von *this von operator() ermöglicht die Verkettung von operator(), wie operator<< auf std::ostreams

template<typename T> struct map_init_helper
{
    T& data;
    map_init_helper(T& d) : data(d) {}
    map_init_helper& operator() (typename T::key_type const& key, typename T::mapped_type const& value)
    {
        data[key] = value;
        return *this;
    }
};

template<typename T> map_init_helper<T> map_init(T& item)
{
    return map_init_helper<T>(item);
}

Da die Funktion und die Hilfsklasse in einer Vorlage dargestellt werden, können Sie sie für jede Karte oder Kartenstruktur verwenden. Das heißt es kann auch Einträge zu std::unordered_map hinzufügen

Wenn Sie diese Helfer nicht schreiben möchten, bietet boost :: assign die gleiche Funktionalität.

57
MSalters

MSalters-Lösung ist eine gute Lösung, implementiert boost::assign::map_list_of jedoch im Wesentlichen neu. Wenn Sie Boost haben, können Sie es direkt verwenden:

#include <boost/assign/list_of.hpp>
#include <boost/unordered_map.hpp>
#include <iostream>

using boost::assign::map_list_of;

enum eee { AA,BB,CC };

const boost::unordered_map<eee,const char*> eeeToString = map_list_of
    (AA, "AA")
    (BB, "BB")
    (CC, "CC");

int main()
{
    std::cout << " enum AA = " << eeeToString.at(AA) << std::endl;
    return 0;
}
29
Alastair

Ein Formular automatisch aus einem anderen generieren.

Quelle:

enum {
  VALUE1, /* value 1 */
  VALUE2, /* value 2 */
};

Generiert:

const char* enum2str[] = {
  "value 1", /* VALUE1 */
  "value 2", /* VALUE2 */
};

Wenn die Aufzählungswerte groß sind, kann ein generiertes Formular unordered_map <> oder Vorlagen verwenden, wie von Constantin vorgeschlagen.

Quelle:

enum State{
  state0 = 0, /* state 0 */
  state1 = 1, /* state 1 */
  state2 = 2, /* state 2 */
  state3 = 4, /* state 3 */

  state16 = 0x10000, /* state 16 */
};

Generiert:

template <State n> struct enum2str { static const char * const value; };
template <State n> const char * const enum2str<n>::value = "error";

template <> struct enum2str<state0> { static const char * const value; };
const char * const enum2str<state0>::value = "state 0";

Beispiel:

#include <iostream>

int main()
{
  std::cout << enum2str<state16>::value << std::endl;
  return 0;
}
19
jfs

Ich erinnere mich, dass ich das an anderer Stelle in StackOverflow beantwortet habe. Wiederholt es hier. Grundsätzlich handelt es sich um eine Lösung, die auf variadischen Makros basiert und ziemlich einfach zu bedienen ist:

#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 es in Ihrem Code zu verwenden, machen Sie einfach Folgendes: 

AWESOME_MAKE_ENUM(Animal,
    DOG,
    CAT,
    HORSE
);
auto dog = Animal::DOG;
std::cout<<dog;
11
Debdatta Basu

Ich empfehle eine Mischung aus X-Makros, die beste Lösung und die folgenden Vorlagenfunktionen:

Marcinkoziukmyopenidcom ausleihen und verlängern

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

template <class T> T str2enum( const char* );
template <class T> const char* enum2str( T );

#define STR2ENUM(TYPE,ARRAY) \
template <> \
TYPE str2enum<TYPE>( const char* str ) \
    { \
    for( int i = 0; i < (sizeof(ARRAY)/sizeof(ARRAY[0])); i++ ) \
        if( !strcmp( ARRAY[i], str ) ) \
            return TYPE(i); \
    return TYPE(0); \
    }

#define ENUM2STR(TYPE,ARRAY) \
template <> \
const char* enum2str<TYPE>( TYPE v ) \
    { \
    return ARRAY[v]; \
    }

#define ENUMANDSTR(TYPE,ARRAY)\
    STR2ENUM(TYPE,ARRAY) \
    ENUM2STR(TYPE,ARRAY)

ENUMANDSTR(Colours,colours_str)

color.def

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

Ich benutze diese Lösung, die ich unten wiedergebe:

#define MACROSTR(k) #k

#define X_NUMBERS \
       X(kZero  ) \
       X(kOne   ) \
       X(kTwo   ) \
       X(kThree ) \
       X(kFour  ) \
       X(kMax   )

enum {
#define X(Enum)       Enum,
    X_NUMBERS
#undef X
} kConst;

static char *kConstStr[] = {
#define X(String) MACROSTR(String),
    X_NUMBERS
#undef X
};

int main(void)
{
    int k;
    printf("Hello World!\n\n");

    for (k = 0; k < kMax; k++)
    {
        printf("%s\n", kConstStr[k]);
    }

    return 0;
}

Wenn Sie String-Repräsentationen von MyEnum variables abrufen möchten, werden sie von Vorlagen nicht geschnitten. Template kann auf ganzzahlige Werte spezialisiert sein, die zur Kompilierzeit bekannt sind.

Wenn es jedoch das ist, was Sie wollen, dann versuchen Sie:

#include <iostream>

enum MyEnum { VAL1, VAL2 };

template<MyEnum n> struct StrMyEnum {
    static char const* name() { return "Unknown"; }
};

#define STRENUM(val, str) \
  template<> struct StrMyEnum<val> { \
    static char const* name() { return str; }};

STRENUM(VAL1, "Value 1");
STRENUM(VAL2, "Value 2");

int main() {
  std::cout << StrMyEnum<VAL2>::name();
}

Dies ist ausführlich, fängt jedoch Fehler auf, die Sie in Frage stellen - Ihr case VAL1 ist doppelt vorhanden.

3
Constantin

Ihre Antworten haben mich dazu inspiriert, selbst einige Makros zu schreiben. Meine Anforderungen waren folgende:

  1. schreiben Sie jeden Wert der Aufzählung nur einmal, sodass keine doppelten Listen vorhanden sind

  2. bewahren Sie die Aufzählungswerte nicht in einer separaten Datei auf, die später in # enthalten ist, damit ich sie schreiben kann, wo immer Sie möchten

  3. ersetzen Sie das Enum nicht selbst, ich möchte immer noch den Enum-Typ definieren, aber zusätzlich möchte ich in der Lage sein, jeden Enumnenamen der entsprechenden Zeichenfolge zuzuordnen (um nicht auf älteren Code zu wirken).

  4. die Suche sollte schnell sein, also vorzugsweise kein Fallbeispiel für diese riesigen Aufzählungen

Dieser Code erstellt eine klassische Aufzählung mit einigen Werten. Darüber hinaus erstellt es als std :: map, das jeden Aufzählungswert seinem Namen zuordnet (d. H. Map [E_SUNDAY] = "E_SUNDAY" usw.).

Ok, hier ist der Code jetzt:

EnumUtilsImpl.h :

map<int, string> & operator , (map<int, string> & dest, 
                               const pair<int, string> & keyValue) {
    dest[keyValue.first] = keyValue.second; 
    return dest;
}

#define ADD_TO_MAP(name, value) pair<int, string>(name, #name)

EnumUtils.h // Dies ist die Datei, die Sie einschließen möchten, wenn Sie dies tun müssen. Sie verwenden die Makros davon:

#include "EnumUtilsImpl.h"
#define ADD_TO_ENUM(name, value) \
    name value

#define MAKE_ENUM_MAP_GLOBAL(values, mapName) \
    int __makeMap##mapName() {mapName, values(ADD_TO_MAP); return 0;}  \
    int __makeMapTmp##mapName = __makeMap##mapName();

#define MAKE_ENUM_MAP(values, mapName) \
    mapName, values(ADD_TO_MAP);

MyProjectCodeFile.h // Dies ist ein Beispiel für die Verwendung einer benutzerdefinierten Enumeration:

#include "EnumUtils.h*

#define MyEnumValues(ADD) \
    ADD(val1, ), \
    ADD(val2, ), \
    ADD(val3, = 100), \
    ADD(val4, )

enum MyEnum {
    MyEnumValues(ADD_TO_ENUM)
};

map<int, string> MyEnumStrings;
// this is how you initialize it outside any function
MAKE_ENUM_MAP_GLOBAL(MyEnumValues, MyEnumStrings); 

void MyInitializationMethod()
{ 
    // or you can initialize it inside one of your functions/methods
    MAKE_ENUM_MAP(MyEnumValues, MyEnumStrings); 
}

Prost.

2
muqker

Ich wäre versucht, eine Karte m zu haben - und diese in das Enum einzubetten.

setup mit mE Enum.VAL 1] = "Wert 1";

und alles ist fertig.

2

Ich habe diese Funktionalität mehrmals benötigt, um Code von anderen zu debuggen/zu analysieren. Dazu habe ich ein Perl-Skript geschrieben, das eine Klasse mit mehreren überladenen toString-Methoden generiert. Jede toString-Methode verwendet eine Enum als Argument und gibt const char* zurück.

Natürlich analysiert das Skript C++ nicht für Enumerationen selbst, sondern verwendet ctags zum Generieren der Symboltabelle.

Das Perl-Skript ist hier: http://heinitz-it.de/download/enum2string/enum2string.pl.html

2

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!

2
OlivierB
typedef enum {
    ERR_CODE_OK = 0,
    ERR_CODE_SNAP,

    ERR_CODE_NUM
} ERR_CODE;

const char* g_err_msg[ERR_CODE_NUM] = {
    /* ERR_CODE_OK   */ "OK",
    /* ERR_CODE_SNAP */ "Oh, snap!",
};

Oben ist meine einfache Lösung. Ein Vorteil davon ist die 'NUM', die die Größe des Nachrichten-Arrays steuert und auch den Zugriff außerhalb der Grenzen verhindert (wenn Sie dies sinnvoll einsetzen).

Sie können auch eine Funktion definieren, um die Zeichenfolge abzurufen:

const char* get_err_msg(ERR_CODE code) {
    return g_err_msg[code];
}

Neben meiner Lösung fand ich die folgende sehr interessant. In der Regel wurde das Synchronisierungsproblem des obigen gelöst.

Folien hier: http://www.slideshare.net/arunksaha/touchless-enum-tostring-28684724

Code hier: https://github.com/arunksaha/enum_to_string

1
Madwyn

Ich wollte nur diese mögliche elegante Lösung mit Makros zeigen. Das löst das Problem nicht, aber ich denke, es ist ein guter Weg, um das Problem zu überdenken.

#define MY_LIST(X) X(value1), X(value2), X(value3)

enum eMyEnum
    {
    MY_LIST(PLAIN)
    };

const char *szMyEnum[] =
    {
    MY_LIST(STRINGY)
    };


int main(int argc, char *argv[])
{

std::cout << szMyEnum[value1] << value1 <<" " <<  szMyEnum[value2] << value2 << std::endl;

return 0;
}

---- BEARBEITEN ----

Nach einigen Recherchen im Internet und eigenen Experimenten kam ich zu folgender Lösung:

//this is the enum definition
#define COLOR_LIST(X) \
  X( RED    ,=21)      \
  X( GREEN  )      \
  X( BLUE   )      \
  X( PURPLE , =242)      \
  X( ORANGE )      \
  X( YELLOW )

//these are the macros
#define enumfunc(enums,value) enums,
#define enumfunc2(enums,value) enums value,
#define ENUM2SWITCHCASE(enums) case(enums): return #enums;

#define AUTOENUM(enumname,listname) enum enumname{listname(enumfunc2)};
#define ENUM2STRTABLE(funname,listname) char* funname(int val) {switch(val) {listname(ENUM2SWITCHCASE) default: return "undef";}}
#define ENUM2STRUCTINFO(spacename,listname) namespace spacename { int values[] = {listname(enumfunc)};int N = sizeof(values)/sizeof(int);ENUM2STRTABLE(enum2str,listname)};

//here the enum and the string enum map table are generated
AUTOENUM(testenum,COLOR_LIST)
ENUM2STRTABLE(testfunenum,COLOR_LIST)
ENUM2STRUCTINFO(colorinfo,COLOR_LIST)//colorinfo structur {int values[]; int N; char * enum2str(int);}

//debug macros
#define str(a) #a
#define xstr(a) str(a)


int main( int argc, char** argv )
{
testenum x = YELLOW;
std::cout << testfunenum(GREEN) << "   " << testfunenum(PURPLE) << PURPLE << "  " << testfunenum(x);

for (int i=0;i< colorinfo::N;i++)
std::cout << std::endl << colorinfo::values[i] <<  "  "<< colorinfo::enum2str(colorinfo::values[i]);

  return EXIT_SUCCESS;
}

Ich wollte es nur posten, vielleicht könnte jemand diese Lösung nützlich finden. Es sind keine Vorlagenklassen, kein Bedarf an C++ 11 und kein Boost erforderlich, daher könnte dies auch für einfaches C verwendet werden.

---- EDIT2 ----

die Informationstabelle kann zu Problemen führen, wenn mehr als 2 Enumerationen verwendet werden (Compiler-Problem). Die folgende Problemumgehung funktionierte:

#define ENUM2STRUCTINFO(spacename,listname) namespace spacename { int spacename##_##values[] = {listname(enumfunc)};int spacename##_##N = sizeof(spacename##_##values)/sizeof(int);ENUM2STRTABLE(spacename##_##enum2str,listname)};
1
jamk

in der Kopfzeile:

enum EFooOptions
 {
FooOptionsA = 0, EFooOptionsMin = 0,
FooOptionsB,
FooOptionsC,
FooOptionsD 
EFooOptionsMax
};
extern const wchar* FOO_OPTIONS[EFooOptionsMax];

in der .cpp-Datei:

const wchar* FOO_OPTIONS[] = {
    L"One",
    L"Two",
    L"Three",
    L"Four"
};

Vorbehalt: Behandeln Sie keinen schlechten Array-Index. :) Sie können jedoch problemlos eine Funktion hinzufügen, um die Aufzählung zu überprüfen, bevor Sie den String aus dem Array abrufen.

0
moogs

Prüfen Sie, ob die folgende Syntax zu Ihnen passt:

// WeekEnd enumeration
enum WeekEnd
{
    Sunday = 1,
    Saturday = 7
};

// String support for WeekEnd
Begin_Enum_String( WeekEnd )
{
    Enum_String( Sunday );
    Enum_String( Saturday );
}
End_Enum_String;

// Convert from WeekEnd to string
const std::string &str = EnumString<WeekEnd>::From( Saturday );
// str should now be "Saturday"

// Convert from string to WeekEnd
WeekEnd w;
EnumString<WeekEnd>::To( w, "Sunday" );
// w should now be Sunday

Wenn ja, dann sollten Sie diesen Artikel auschecken:
http://www.gamedev.net/reference/snippets/features/cppstringizing/

0
Francis Xavier

dieses richtige alte Durcheinander ist meine Anstrengung, die auf den Stücken von SO basiert. For_each müsste erweitert werden, um mehr als 20 Aufzählungswerte zu unterstützen. Getestet auf Visual Studio 2019, Clang und GCC. c ++ 11

#define _enum_expand(arg) arg
#define _enum_select_for_each(_,_0, _1, _2,_3,_4, _5, _6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,N, ...) N
#define _enum_for_each_0(_call, arg0,arg1,...)
#define _enum_for_each_1(_call, arg0,arg1) _call(arg0,arg1)
#define _enum_for_each_2(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_1(_call,arg0, __VA_ARGS__))
#define _enum_for_each_3(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_2(_call,arg0, __VA_ARGS__))
#define _enum_for_each_4(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_3(_call,arg0, __VA_ARGS__))
#define _enum_for_each_5(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_4(_call,arg0, __VA_ARGS__))
#define _enum_for_each_6(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_5(_call,arg0, __VA_ARGS__))
#define _enum_for_each_7(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_6(_call,arg0, __VA_ARGS__))
#define _enum_for_each_8(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_7(_call,arg0, __VA_ARGS__))
#define _enum_for_each_9(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_8(_call,arg0, __VA_ARGS__))
#define _enum_for_each_10(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_9(_call,arg0, __VA_ARGS__))
#define _enum_for_each_11(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_10(_call,arg0, __VA_ARGS__))
#define _enum_for_each_12(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_11(_call,arg0, __VA_ARGS__))
#define _enum_for_each_13(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_12(_call,arg0, __VA_ARGS__))
#define _enum_for_each_14(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_13(_call,arg0, __VA_ARGS__))
#define _enum_for_each_15(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_14(_call,arg0, __VA_ARGS__))
#define _enum_for_each_16(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_15(_call,arg0, __VA_ARGS__))
#define _enum_for_each_17(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_16(_call,arg0, __VA_ARGS__))
#define _enum_for_each_18(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_17(_call,arg0, __VA_ARGS__))
#define _enum_for_each_19(_call, arg0,arg1, ...) _call(arg) _enum_expand(_enum_for_each_18(_call,arg0, __VA_ARGS__))
#define _enum_for_each(arg, ...) \
    _enum_expand(_enum_select_for_each(_, ##__VA_ARGS__, \
    _enum_for_each_19, _enum_for_each_18, _enum_for_each_17, _enum_for_each_16, _enum_for_each_15, \
    _enum_for_each_14, _enum_for_each_13, _enum_for_each_12, _enum_for_each_11, _enum_for_each_10, \
    _enum_for_each_9,  _enum_for_each_8,  _enum_for_each_7,  _enum_for_each_6,  _enum_for_each_5,  \
    _enum_for_each_4,  _enum_for_each_3,  _enum_for_each_2,  _enum_for_each_1,  _enum_for_each_0)(arg, ##__VA_ARGS__))

#define _enum_strip_args_1(arg0) arg0
#define _enum_strip_args_2(arg0, arg1) arg0, arg1
#define _enum_make_args(...) (__VA_ARGS__)

#define _enum_elem_arity1_1(arg) arg,
#define _enum_elem_arity1( ...) _enum_expand(_enum_elem_arity1_1 __VA_ARGS__)
#define _enum_elem_arity2_1(arg0,arg1) arg0 = arg1,
#define _enum_elem_arity2( ...) _enum_expand(_enum_elem_arity2_1 __VA_ARGS__)

#define _enum_elem_select_arity_2(_0, _1, NAME,...) NAME
#define _enum_elem_select_arity_1(...) _enum_expand(_enum_elem_select_arity_2(__VA_ARGS__, _enum_elem_arity2,_enum_elem_arity1,_))
#define _enum_elem_select_arity(enum_type,...) _enum_expand(_enum_elem_select_arity_1 __VA_ARGS__)(__VA_ARGS__)

#define _enum_str_arity1_1(enum_type,arg) { enum_type::arg,#arg },
#define _enum_str_arity1(enum_type,...) _enum_expand(_enum_str_arity1_1 _enum_make_args( enum_type, _enum_expand(_enum_strip_args_1 __VA_ARGS__)))
#define _enum_str_arity2_1(enum_type,arg,value) { enum_type::arg,#arg },
#define _enum_str_arity2(enum_type, ...) _enum_expand(_enum_str_arity2_1 _enum_make_args( enum_type, _enum_expand(_enum_strip_args_2 __VA_ARGS__)))
#define _enum_str_select_arity_2(_0, _1, NAME,...) NAME
#define _enum_str_select_arity_1(...) _enum_expand(_enum_str_select_arity_2(__VA_ARGS__, _enum_str_arity2,_enum_str_arity1,_))
#define _enum_str_select_arity(enum_type,...) _enum_expand(_enum_str_select_arity_1 __VA_ARGS__)(enum_type,__VA_ARGS__)

#define error_code_enum(enum_type,...)  enum class enum_type {              \
    _enum_expand(_enum_for_each(_enum_elem_select_arity,enum_type, ##__VA_ARGS__))};  \
    namespace _ ## enum_type ## _detail { \
        template <typename> struct _ ## enum_type ## _error_code{ \
            static const std::map<enum_type, const char*> enum_type ## _map; \
        }; \
            template <typename T> \
            const std::map<enum_type, const char*> _ ## enum_type ## _error_code<T>::enum_type ## _map = { \
                _enum_expand(_enum_for_each(_enum_str_select_arity,enum_type,  ##__VA_ARGS__)) \
        }; \
    } \
    inline const char* get_error_code_name(const enum_type& value) { \
        return _ ## enum_type ## _detail::_ ## enum_type ## _error_code<enum_type>::enum_type ## _map.find(value)->second; \
    } 

error_code_enum(myenum,
    (one, 1),
    (two)
);

das erzeugt den folgenden Code

enum class myenum { 
    one = 1,
    two,
};
namespace _myenum_detail {
    template <typename>
    struct _myenum_error_code {
        static const std::map<myenum, const char*> myenum_map;
    };
    template <typename T>
    const std::map<myenum, const char*> _myenum_error_code<T>::myenum_map = {
        { myenum::one, "one" }, 
        { myenum::two, "two" },
    };
}
inline const char* get_error_code_name(const myenum& value) { 
    return _myenum_detail::_myenum_error_code<myenum>::myenum_map.find(value)->second; 
}

Wie schade, dass Sie mit dem Präprozessor springen müssen, um dies in einer der am häufigsten verwendeten Programmiersprachen der Welt zu tun ...

0
rmawatson

Ich habe mehr Zeit damit verbracht, dieses Thema zu recherchieren, das ich gerne zugeben möchte. Glücklicherweise gibt es großartige Open Source-Lösungen in freier Wildbahn.

Dies sind zwei großartige Ansätze, auch wenn sie (noch) nicht ausreichend bekannt sind.

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

  • Reflektive 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.
0

Ich hatte kürzlich das gleiche Problem mit einer Lieferantenbibliothek (Fincad). Glücklicherweise stellte der Anbieter für alle Aufzählungen eine XML-Doucumentation bereit. Am Ende habe ich eine Map für jeden Enummentyp erstellt und für jedes Enum eine Lookup-Funktion bereitgestellt. Mit dieser Technik können Sie auch eine Suche außerhalb des Enumebereichs abfangen.

Ich bin sicher, swig könnte etwas Ähnliches für Sie tun, aber ich bin froh, die in Ruby geschriebenen Code-Generierungs-Utils zur Verfügung zu stellen.

Hier ist ein Beispiel für den Code:

std::map<std::string, switches::FCSW2::type> init_FCSW2_map() {
        std::map<std::string, switches::FCSW2::type> ans;
        ans["Act365Fixed"] = FCSW2::Act365Fixed;
        ans["actual/365 (fixed)"] = FCSW2::Act365Fixed;
        ans["Act360"] = FCSW2::Act360;
        ans["actual/360"] = FCSW2::Act360;
        ans["Act365Act"] = FCSW2::Act365Act;
        ans["actual/365 (actual)"] = FCSW2::Act365Act;
        ans["ISDA30360"] = FCSW2::ISDA30360;
        ans["30/360 (ISDA)"] = FCSW2::ISDA30360;
        ans["ISMA30E360"] = FCSW2::ISMA30E360;
        ans["30E/360 (30/360 ISMA)"] = FCSW2::ISMA30E360;
        return ans;
}
switches::FCSW2::type FCSW2_lookup(const char* fincad_switch) {
        static std::map<std::string, switches::FCSW2::type> switch_map = init_FCSW2_map();
        std::map<std::string, switches::FCSW2::type>::iterator it = switch_map.find(fincad_switch);
        if(it != switch_map.end()) {
                return it->second;
        } else {
                throw FCSwitchLookupError("Bad Match: FCSW2");
        }
}

Anscheinend möchten Sie den umgekehrten Weg gehen (Enum für String statt String für Enum). Dies sollte jedoch trivial sein.

-Whit

0
Whit Armstrong