So ordnen Sie C ++ - Aufzählungen einfach Zeichenfolgen zu


119

Ich habe eine Reihe von Aufzählungstypen in einigen Bibliotheksheaderdateien, die ich verwende, und ich möchte eine Möglichkeit haben, Aufzählungswerte in Benutzerzeichenfolgen zu konvertieren - und umgekehrt.

RTTI wird das für mich nicht tun, da die 'Benutzerzeichenfolgen' etwas besser lesbar sein müssen als die Aufzählungen.

Eine Brute-Force-Lösung wäre eine Reihe solcher Funktionen, aber ich denke, das ist ein bisschen zu C-ähnlich.

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 darum kümmern.

UPDATE: Vielen Dank für Ihre Vorschläge. Ich hätte klarstellen müssen, dass die Aufzählungen in einem Bibliotheksheader eines Drittanbieters definiert sind, damit ich die Definition nicht ändern muss.

Mein Bauchgefühl ist es 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);

Antworten:


60

Wenn Sie möchten, dass die Aufzählungsnamen selbst als Zeichenfolgen verwendet werden, lesen Sie diesen Beitrag . Ansonsten astd::map<MyEnum, char const*> funktionieren. (Es macht keinen Sinn, Ihre String-Literale in std :: strings in der Map zu kopieren.)

Für zusätzlichen syntaktischen Zucker schreiben Sie hier eine map_init-Klasse. Das Ziel ist 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 a zurück map_init_helper<T>. map_init_helper<T>speichert ein T & und definiert das Triviale map_init_helper& operator()(typename T::key_type const&, typename T::value_type const&). (Die Rückkehr *thisvon operator()ermöglicht das Verketten von operator(), wie operator<<bei 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 als Vorlagen verwendet werden, können Sie sie für jede Karte oder kartenähnliche Struktur verwenden. Dh es können auch Einträge hinzugefügt werdenstd::unordered_map

Wenn Sie diese Helfer nicht gerne schreiben, bietet boost :: assign sofort die gleiche Funktionalität.


Sie haben Recht, auf eine andere Frage zu verweisen. Leute sollten einen Blick in die "verwandten Fragen"
werfen,

2
@xtofl: Die hier gezeigten "verwandten Fragen" unterscheiden sich grundlegend von den verwandten Fragen, die beim Posten der Frage aufgeführt wurden!
Roddy

@MSalters, eine std :: map ist eine nützliche Methode, um die Implementierung zu handhaben, aber ich suche nach Möglichkeiten, um den erforderlichen Boilerplate-Code zu reduzieren.
Roddy

@MSalters, es wäre schön, mehrere Argumente für operator [] akzeptieren zu können. aber leider kann man das nicht tun. x [a, b] zu x [b] auswerten. Der Ausdruck (a, b) verwendet den Kommaoperator. Es entspricht also ["A"] ["B"] ["C"] in Ihrem Code. Sie könnten es ändern, um [eValue1] ["A"] [eValu ..
Johannes Schaub - litb

Der Funktionsaufrufoperator wäre auch ein guter Kandidat: map_init (MyMap) (eValue1, "A") (eValue2, "B") .... dann ist es gleichbedeutend mit boost :: assign: insert (MyMap) (eValue1, "A") (eValue2, "B") ... ( boost.org/doc/libs/1_35_0/libs/assign/doc/index.html )
Johannes Schaub - litb

31

Die MSalters-Lösung ist gut, wird jedoch grundsätzlich neu implementiert boost::assign::map_list_of. 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;
}

Wie würden Sie dies verwenden, wenn eeeToString ein Datenelement einer Klasse ist? Ich erhalte die Meldung "Fehler: Initialisierung der Datenelemente ist nicht zulässig"
Benutzer

@User: Klassendatenelemente werden in Konstruktoren initialisiert, normalerweise in der Initialisierungsliste.
MSalters

Gibt es eine Möglichkeit, diese Funktion für alle Aufzählungen zu verwenden? Ich habe mehrere Aufzählungsdeklarationen und möchte nicht, dass die Karte eeein Ihrem Fall nur für den Typ funktioniert .
Justin Liang

Ich habe versucht, eine Vorlage zu verwenden, aber dann bekam und Fehler : error: template declaration of 'const boost::unordered::unordered_map<T, const char*> enumToString'.
Justin Liang

4
Tatsächlich ist diese Antwort mit C ++ 11 weitgehend veraltet.
Alastair

19

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

Es ist zwar am schnellsten, aber nicht so einfach wie @MSalters.
Kenny

2
Wenn Sie ein bisschen Perl / Python haben, können Sie eine Liste von Zeichenfolgen aus einer Textdatei lesen und zur Kompilierungszeit eine .h-Datei mit dem statischen Zeichen generieren. = "Programme schreiben, um Programme zu schreiben"
Martin Beckett

@mgb: Perl / Python sind nicht die einzigen Optionen, die fast jede Vorlagen-Engine in einer beliebigen Sprache bietet (in diesem Fall werden beide Formulare aus einer Vorlage generiert).
JFS

@jf. Ja, der wichtige Punkt war, statische Datentabellen zur Kompilierungszeit automatisch zu erstellen. Ich würde wahrscheinlich lieber nur ein dummes statisches Array erzeugen.
Martin Beckett

Funktioniert dies, wenn der Status zur Kompilierungszeit nicht bekannt ist? Ich bin mir ziemlich sicher, dass dies nicht der Fall ist. Theoretisch müsste der Compiler die enum2str-Vorlage mit allen möglichen Werten der Aufzählung instanziieren, was ich ziemlich sicher bin, dass gcc (zumindest) nicht funktioniert.
Alastair

11

Ich erinnere mich, dass ich dies an anderer Stelle auf StackOverflow beantwortet habe. Wiederholen Sie es hier. Grundsätzlich handelt es sich um eine Lösung, die auf verschiedenen Makros basiert und recht einfach zu verwenden 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, gehen Sie einfach wie folgt vor:

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

1
Ändern Sie einfach die Enum-Klassendeklaration in Enum, um mit Pre C ++ 11 zu arbeiten.
Debdatta Basu

1
Sie haben Recht, es funktioniert (das Auto ist auch nur C ++ 11). Schöne Lösung! Es wäre perfekt, wenn Sie auch einen Wert für einige Aufzählungen
festlegen

Ich denke, ich habe in Boost so etwas gesehen
Sergei

10

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

Marcinkoziukmyopenidcom auszuleihen und zu 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)

colour.def

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

Gibt es eine Möglichkeit, die Definition des Enum-String-Arrays generisch zu gestalten? (Ich weiß nicht, wie man mit einem X-Makro in einem Makro umgeht, und ich kann mit der Vorlage nicht einfach umgehen)
Jonathan

5

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

1
Dies sind grundlegende X-Makros, und ich bin erschüttert, dass dies die erste Antwort hier ist, die dies vorschlägt! +1
Leichtigkeitsrennen im Orbit

4

Wenn Sie Zeichenfolgendarstellungen von MyEnum Variablen erhalten möchten, werden diese von Vorlagen nicht abgeschnitten. Die Vorlage kann auf Integralwerte spezialisiert werden, die zur Kompilierungszeit bekannt sind.

Wenn Sie dies jedoch möchten, versuchen Sie Folgendes:

#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 wie den von Ihnen in Frage gestellten ab - Ihr Fehler case VAL1wird dupliziert.


Eigentlich ist der Methodenname () nicht notwendig. Siehe meine Antwort.
JFS

3

Ich habe mehr Zeit damit verbracht, dieses Thema zu erforschen, als ich 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 bekannt genug sind:

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

Bessere Aufzählungen

  • Reflektierende Enum-Bibliothek zur Kompilierungszeit mit sauberer Syntax, in einer einzelnen Header-Datei und ohne Abhängigkeiten.
  • Einschränkungen: Basierend auf Makros, kann nicht innerhalb einer Klasse verwendet werden.

2

Ich wäre versucht, eine Karte m zu haben - und diese in die Aufzählung einzubetten.

Setup mit m [MyEnum.VAL1] = "Wert 1";

und alles ist erledigt.


2

Ich habe diese Funktionalität mehrmals zum Debuggen / Analysieren von Code von anderen benötigt. Zu diesem Zweck habe ich ein Perl-Skript geschrieben, das eine Klasse mit mehreren überladenen toStringMethoden generiert . Jede toStringMethode nimmt ein Enumals Argument und gibt zurück const char*.

Natürlich analysiert das Skript C ++ nicht nach Aufzählungen selbst, sondern verwendet ctags zum Generieren der Symboltabelle.

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


2

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 gepflegt werden müssen

  2. Bewahren Sie die Aufzählungswerte nicht in einer separaten Datei auf, die später # eingeschlossen wird, damit ich sie schreiben kann, wo immer ich will

  3. Ersetzen Sie nicht die Aufzählung selbst, ich möchte immer noch den Aufzählungstyp definieren, aber zusätzlich möchte ich in der Lage sein, jeden Aufzählungsnamen der entsprechenden Zeichenfolge zuzuordnen (um den Legacy-Code nicht zu beeinflussen).

  4. Die Suche sollte schnell sein, also vorzugsweise kein Switch-Case für diese riesigen Enums

Dieser Code erstellt eine klassische Aufzählung mit einigen Werten. Außerdem wird als std :: map erstellt, die jeden Aufzählungswert seinem Namen zuordnet (dh map [E_SUNDAY] = "E_SUNDAY" usw.)

Ok, hier ist jetzt der Code:

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 dieses Zeug ausführen müssen. Sie werden die Makros daraus verwenden:

#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 zum Erstellen einer benutzerdefinierten Aufzählung:

#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

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!


1

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

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


In der Tat eine sehr nicht DRY-SPOT-Lösung.
xtofl

Jetzt, wo Sie DRY erwähnen. Die .h- und .cpp-Dateien werden automatisch aus einer anderen Eingabedatei generiert. Ich würde gerne bessere Lösungen sehen (die nicht auf unnötige Komplexität zurückgreifen)
Moogs

1

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

#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 Internetrecherchen 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 erforderlich, kein C ++ 11 und kein Boost erforderlich, sodass dies auch für einfaches C verwendet werden kann.

---- EDIT2 ----

Die Informationstabelle kann bei Verwendung von mehr als 2 Aufzählungen zu Problemen führen (Compilerproblem). Die folgende Problemumgehung hat funktioniert:

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

1
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 Nachrichtenarrays steuert und auch den Zugriff außerhalb der Grenzen verhindert (wenn Sie es mit Bedacht verwenden).

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

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

Nach meiner Lösung fand ich dann die folgende ziemlich interessant. Es löste im Allgemeinen das Synchronisationsproblem des obigen.

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

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


1

Ich weiß, dass ich zu spät zum Feiern bin, aber für alle anderen, die diese Seite besuchen, könnten Sie es versuchen, es ist einfacher als alles dort und macht mehr Sinn:

namespace texs {
    typedef std::string Type;
    Type apple = "apple";
    Type wood = "wood";
}

Schlagen Sie vor, Zeichenfolgen und überhaupt keine Aufzählungen zu verwenden? Das löst das Problem nicht wirklich.
Roddy

0

Ich hatte kürzlich das gleiche Problem mit einer Herstellerbibliothek (Fincad). Glücklicherweise hat der Anbieter eine XML-Dokumentation für alle Aufzählungen bereitgestellt. Am Ende habe ich für jeden Aufzählungstyp eine Karte erstellt und für jede Aufzählung eine Suchfunktion bereitgestellt. Mit dieser Technik können Sie auch eine Suche außerhalb des Bereichs der Aufzählung abfangen.

Ich bin mir sicher, dass swig etwas Ähnliches für Sie tun könnte, aber ich bin froh, die in Ruby geschriebenen Dienstprogramme zur Codegenerierung bereitstellen zu können.

Hier ist ein Beispiel des Codes:

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

Scheint, als ob Sie den anderen Weg gehen möchten (Aufzählung zu Zeichenfolge, anstatt Zeichenfolge zu Aufzählung), aber dies sollte trivial sein, um es umzukehren.

-Weiß


1
a) Findet das sonst noch jemand absolut unlesbar? Ein paar typedefs und die Verwendung von Deklarationen würden die Lesbarkeit erheblich verbessern. b) Lokale statische Deklarationen sind nicht threadsicher. c) Verwenden Sie const string & anstelle von char *. d) Wie wäre es, den Wert einzuschließen, der in der ausgelösten Ausnahme nicht gefunden werden konnte?
Alastair

0

Überprü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 dies der Fall ist, sollten Sie diesen Artikel lesen :
http://www.gamedev.net/reference/snippets/features/cppstringizing/


0

Dieses richtige alte Durcheinander ist meine Anstrengung, basierend auf Kleinigkeiten von SO. Das 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)
);

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

So schade, dass man mit dem Präprozessor springen muss, um dies in einer der am häufigsten verwendeten Programmiersprachen der Welt zu tun ...

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.