C'è un modo semplice per convertire l'enumerazione C ++ in stringa?


123

Supponiamo di avere alcune enumerazioni denominate:

enum MyEnum {
      FOO,
      BAR = 0x50
};

Quello per cui ho cercato su Google è uno script (qualsiasi lingua) che analizza tutte le intestazioni del mio progetto e genera un'intestazione con una funzione per enumerazione.

char* enum_to_string(MyEnum t);

E un'implementazione con qualcosa del genere:

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

Il gotcha è davvero con enumerazioni typedefed e enumerazioni in stile C senza nome. Qualcuno sa qualcosa per questo?

EDIT: La soluzione non dovrebbe modificare la mia fonte, ad eccezione delle funzioni generate. Le enumerazioni sono in un'API, quindi utilizzare le soluzioni proposte fino ad ora non è un'opzione.


La risposta sulla fabbrica basata su macro è stata spostata in stackoverflow.com/questions/147267/… - dopo che la domanda è stata aggiornata, non è più rilevante qui.
Suma

Risposte:


48

Potresti voler controllare GCCXML .

L'esecuzione di GCCXML sul codice di esempio produce:

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

Puoi utilizzare qualsiasi lingua che preferisci per estrarre i tag Enumeration ed EnumValue e generare il codice desiderato.


Eccellente! Ha funzionato come un fascino con un semplice script Python. Grazie.
Edu Felipe

6
+1, GCCXML sembra molto carino! (Anche se ho quasi fatto -1 dato che inizialmente ho letto male questo come suggerimento di utilizzare la sintassi XML prolissa sopra per codificare il tuo enum - una soluzione che puzza di overengineering!)
j_random_hacker

1
qualsiasi modifica puoi postare lo script python?
phillipwei

74

Le macro X sono la soluzione migliore. Esempio:

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

Tuttavia, di solito preferisco il seguente metodo, in modo che sia possibile modificare un po 'la stringa.

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

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

11
elegante, anche se non mi piace il file extra
Ronny Brendel

2
Assicurati solo che il tuo processo di compilazione non anteponga #pragma (una volta) prima di ogni file di inclusione ...
xtofl

24
Non sono sicuro della soluzione "migliore"!
Gare di leggerezza in orbita

2
Questa soluzione è semplicemente di gran lunga superiore a qualsiasi caso di switch o basato su array, perché non duplica i nomi, facilitando la modifica dell'enumerazione.
Julien Guertault

2
@ ikku100 su cui hai sbagliato #define X(a, b) #b. Questo è necessario solo se la definizione ha questo aspetto X(Red, red), piuttosto che la definizione mostrata nella risposta,X(Red, "red")
impara il

43

@hydroo: senza il file aggiuntivo:

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

Amo questa soluzione. Tuttavia, sarebbe più chiaro se SOME_UNION e MAKE_UNION si chiamassero SOME_ENUM e MAKE_ENUM.
Bruno Martinez

Questa è un'ottima soluzione. Ho il gestore di risorse C ++ più manutenibile con cui abbia mai avuto a che fare.
DCurro

Devo ringraziarti per questa semplice soluzione :-) - L'ho modificato un po ', però, per MetaSyntacticVariableNames[]far parte di una dichiarazione di classe, creando un metodostatic const char* getNameByEnum(MetaSyntacticVariable e) { /*code to return the static string*/ }
DeckerDK

Risposta fantastica! L'ho semplificato ulteriormente raggruppando MAKE_ENUM e MAKE_STRINGS in un'unica macro, rendendo l'intero processo ancora più semplice. Ho aggiunto una risposta in questo thread con quel codice se qualcuno è interessato.
Francois Bertrand

35

Quello che tendo a fare è creare un array C con i nomi nello stesso ordine e posizione dei valori enum.

per esempio.

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

quindi puoi usare l'array nei punti in cui desideri un valore leggibile dall'uomo, ad es

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

Potresti sperimentare un po 'con l'operatore stringizing (vedi # nel riferimento del tuo preprocessore) che farà quello che vuoi, in alcune circostanze, ad esempio:

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

stamperà "rosso" su stdout. Sfortunatamente non funzionerà per una variabile (poiché otterrai stampato il nome della variabile)


L'ultimo avvertimento (non funzionerà per una variabile) è un grosso svantaggio, ma +1 comunque.
chappjc

3
Funziona solo se non imposti valori numerici speciali per enumerare le voci.
kyb

11

Ho una macro incredibilmente semplice da usare che lo fa in modo completamente SECCO. Comprende macro variadiche e qualche semplice magia di analisi. Ecco qui:

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

Per usarlo nel tuo codice, fai semplicemente:

AWESOME_MAKE_ENUM(Animal,
    DOG,
    CAT,
    HORSE
);

1
Buona idea usare un enum fortemente tipizzato (classe enum). Ecco una demo: cpp.sh/4ife
chappjc

Funziona con enumerazioni / simboli definiti esternamente. Ad esempio, simboli definiti dal sistema operativo o definiti dalla libreria con spazi vuoti nella numerazione?
Jason Harrison

Molto carino, ma non si compila se inserito in una classe (non sono riuscito a capire perché).
AlwaysLearning

Non sono riuscito a farlo compilare in VS2015. Ricevo un avviso e un errore: avviso: commento su più righe [-Wcomment] #define MAKE_ENUM (name, ...) enum class name { VA_ARGS , __COUNT} errore: stray '#' in program std *: string enumName = #name
Craig.Feied il

8

QT è in grado di estrarre quello di (grazie al compilatore di meta oggetti):

QNetworkReply::NetworkError error;

error = fetchStuff();

if (error != QNetworkReply::NoError) {

    QString errorValue;

    QMetaObject meta = QNetworkReply::staticMetaObject;

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

        QMetaEnum m = meta.enumerator(i);

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

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

            break;

        }

    }

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

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

                QMessageBox::Ok);

    box.exec();

    return 1;

}

In Qt ogni classe che ha la macro Q_OBJECT avrà automaticamente un membro statico "staticMetaObject" del tipo QMetaObject. Puoi quindi trovare tutti i tipi di cose interessanti come le proprietà, i segnali, gli slot e in effetti le enumerazioni.

fonte


7

Questo può essere fatto in C ++ 11

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

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

void main()
{
    std::cout << info[AA] << endl
              << info[BB] << endl
              << info[CC] << endl
              << info[DD] << endl;
}

1
Questo non risponde alla domanda dell'OP: stava cercando un modo per generare automaticamente una funzione per restituire il nome di un membro di enum come stringa.
Spettrale

7

Ho appena reinventato questa ruota oggi e ho pensato di condividerla.

Questa implementazione non richiede alcuna modifica al codice che definisce le costanti, che possono essere enumerazioni #defineos o qualsiasi altra cosa che devolve a un numero intero - nel mio caso avevo simboli definiti in termini di altri simboli. Funziona bene anche con valori sparsi. Consente anche più nomi per lo stesso valore, restituendo sempre il primo. L'unico svantaggio è che richiede di creare una tabella delle costanti, che potrebbe diventare obsoleta man mano che ne vengono aggiunte di nuove, ad esempio.

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

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

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

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

Un esempio di come lo useresti:

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

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

La IdToNamefunzione si basa std::lower_boundper eseguire ricerche rapide, che richiedono l'ordinamento della tabella. Se le prime due voci nella tabella non sono in ordine, la funzione la ordinerà automaticamente.

Modifica: un commento mi ha fatto pensare a un altro modo di utilizzare lo stesso principio. Una macro semplifica la generazione di una grande switchdichiarazione.

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

Buona soluzione. Ma per me lo preferirei switch and caseperché è semplice e di facile comprensione.
Deqing

6
#define stringify( name ) # name

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

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

Ulteriore discussione su questo metodo

Trucchi direttiva del preprocessore per i nuovi arrivati


4
In realtà questo è abbastanza inutile, poiché il metodo stringify è in fase di compilazione ed è abbastanza letterale. Se dici di avere il tipo enum in questione all'interno di una variabile, il tentativo di stringere la variabile ti darà solo il nome della variabile, non il nome del tipo enum.
srcspider

5

È interessante vedere il numero di modi. eccone uno che ho usato molto tempo fa:

nel file 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;

Non è costante, ma è conveniente.

Ecco un altro modo che utilizza le funzionalità di C ++ 11. Questo è const, non eredita un contenitore STL ed è un po 'più ordinato:

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

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

int main()
{
//  std::cout<< enum_to_str( 46 ) << std::endl; //compilation will fail
    std::cout<< "Invalid enum to string : [" << enum_to_str( test(46) ) << "]"<<std::endl; //returns an empty string
    std::cout<< "Enumval five to string : ["<< enum_to_str( five ) << "] "<< std::endl; //works
    return 0;
}

1
È perfettamente legale. Lo faccio tutto il tempo.
Jonathan Graehl

Buona soluzione. Questo è c ++ quindi usare stl map è ok.
Adam Bruss

4
#include <stdarg.h>
#include <algorithm>
#include <string> 
#include <vector>
#include <sstream>
#include <map>

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

Uso:

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

1
Mi piace la tua API, ma sfortunatamente il tuo SmartEnum non crea effettivamente un "tipo" enum. Non puoi farlo MyEnum x = MyEnum::TWO;. Ho pubblicato la mia modifica della tua classe per supportarlo.
Mark Lakata

4

La soluzione macro di Suma è carina. Tuttavia, non è necessario disporre di due macro diverse. Il C ++ includerà felicemente un'intestazione due volte. Basta lasciare fuori la guardia di inclusione.

Quindi avresti una definizione di foobar.h solo

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

e lo includeresti in questo modo:

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

enumfactory.h farà 2 #include ENUMFACTORY_ARGUMENTs. Nel primo round, si espande ENUM come quello di Suma DECLARE_ENUM; nel secondo round ENUM funziona come DEFINE_ENUM.

Puoi anche includere enumfactory.h più volte, purché passi diversi # define per ENUMFACTORY_ARGUMENT


sembra che Suma abbia spostato la risposta qui . Puoi includere il link nella tua risposta. Ho trovato il commento solo per caso e senza rispondere a questa domanda è piuttosto inutile
idclev 463035818

3

Nota che la tua funzione di conversione dovrebbe idealmente restituire un const char *.

Se puoi permetterti di mettere le tue enumerazioni nei loro file di intestazione separati, potresti forse fare qualcosa del genere con le macro (oh, questo sarà brutto):

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

Dove enum_def.h ha:

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

E enum_conv.h ha:

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

E infine, colour.h ha:

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

E puoi usare la funzione di conversione come:

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

Questo è brutto, ma è l'unico modo (a livello di preprocessore) che ti consente di definire l'enum in un unico punto del codice. Il tuo codice quindi non è soggetto a errori dovuti a modifiche all'enum. La tua definizione enum e la funzione di conversione saranno sempre sincronizzate. Tuttavia, ripeto, questo è brutto :)


3

Un'altra risposta: in alcuni contesti, ha senso definire l'enumerazione in un formato non di codice, come un file CSV, YAML o XML, e quindi generare sia il codice di enumerazione C ++ che il codice to-string dalla definizione. Questo approccio può o non può essere pratico nella tua applicazione, ma è qualcosa da tenere a mente.


3

Questa è una modifica alla risposta @ user3360260. Ha le seguenti nuove funzionalità

  • MyEnum fromString(const string&) supporto
  • si compila con VisualStudio 2012
  • l'enum è un tipo POD effettivo (non solo dichiarazioni const), quindi puoi assegnarlo a una variabile.
  • aggiunta la funzionalità "range" di C ++ (in forma di vettore) per consentire l'iterazione "foreach" su enum

Uso:

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

Ecco il codice

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

Si noti che la conversione in Stringa è una ricerca rapida, mentre la conversione in Stringa è una ricerca lineare lenta. Ma le stringhe sono comunque così costose (e il file IO associato), non ho sentito il bisogno di ottimizzare o utilizzare un bimap.


Tu e user3360260 avete una buona soluzione. Perché invece non avere una multimappa?
Vincent

3

Ecco una soluzione in un file (basata sull'elegante risposta di @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

Lo faccio con classi wrapper enum side-by-side separate che vengono generate con macro. Ci sono diversi vantaggi:

  • Può generarli per enumerazioni che non definisco (ad esempio: enumerazioni dell'intestazione della piattaforma del sistema operativo)
  • Può incorporare il controllo dell'intervallo nella classe wrapper
  • Può eseguire una formattazione più "intelligente" con le enumerazioni dei campi di bit

Lo svantaggio, ovviamente, è che ho bisogno di duplicare i valori enum nelle classi del formattatore e non ho alcuno script per generarli. A parte questo, però, sembra funzionare abbastanza bene.

Ecco un esempio di un'enumerazione dalla mia base di codice, senza tutto il codice del framework che implementa le macro ei modelli, ma puoi avere l'idea:

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

L'idea quindi è che invece di usare EHelpLocation, tu usi SEHelpLocation; tutto funziona allo stesso modo, ma ottieni il controllo dell'intervallo e un metodo 'Format ()' sulla variabile enum stessa. Se è necessario formattare un valore autonomo, è possibile utilizzare CEnumFormatter_EHelpLocation :: FormatEnum (...).

Spero che questo sia utile. Mi rendo conto che anche questo non affronta la domanda originale su uno script per generare effettivamente l'altra classe, ma spero che la struttura aiuti qualcuno che cerca di risolvere lo stesso problema o scrivere uno script del genere.


2

È un software inedito ma sembra che BOOST_ENUM di Frank Laub potrebbe andare bene. La parte che mi piace è che puoi definire un'enumerazione nell'ambito di una classe che la maggior parte delle enumerazioni basate su Macro di solito non ti consente di fare. Si trova nel Boost Vault all'indirizzo: http://www.boostpro.com/vault/index.php?action=downloadfile&filename=enum_rev4.6.zip&directory=& Non ha visto alcuno sviluppo dal 2006 quindi non lo faccio sapere quanto bene si compila con le nuove versioni di Boost. Guarda sotto libs / test per un esempio di utilizzo.


2

Questa era la mia soluzione con 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());                        \
    }

Per creare enum, dichiara:

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

Per le conversioni:

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

2

Voglio postare questo nel caso qualcuno lo trovi utile.

Nel mio caso, ho semplicemente bisogno di generare ToString()e FromString()funzioni per un singolo enum C ++ 11 da un singolo .hppfile.

Ho scritto uno script Python che analizza il file di intestazione contenente gli elementi enum e genera le funzioni in un nuovo .cppfile.

Puoi aggiungere questo script in CMakeLists.txt con execute_process o come evento di pre-compilazione in Visual Studio. Il .cppfile verrà generato automaticamente, senza la necessità di aggiornarlo manualmente ogni volta che viene aggiunto un nuovo elemento enum.

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)

Esempio:

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

Correre python generate_enum_strings.py ErrorCode.hpp

Risultato:

ErrorCode.cpp

#include "ErrorCode.hpp"

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

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

        return ErrorCode::Last;
    }
}


2

Aggiungendo ancora più semplicità d'uso alla fantastica risposta di Jasper Bekkers :

Configura una volta:

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

Quindi, per l'utilizzo:

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

2

Potresti usare una libreria di riflessioni, come Ponder . Registri le enumerazioni e poi puoi convertirle avanti e indietro con l'API.

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

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

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

zero.name(); // -> "Zero"

1

Un problema con la risposta 0 è che i valori binari enum non iniziano necessariamente da 0 e non sono necessariamente contigui.

Quando ne ho bisogno, di solito:

  • inserire la definizione enum nella mia fonte
  • modificalo per ottenere solo i nomi
  • fai una macro per cambiare il nome alla clausola case nella domanda, anche se tipicamente su una riga: case foo: return "foo";
  • aggiungi lo switch, il default e altra sintassi per renderlo legale

1

Il seguente script ruby ​​tenta di analizzare le intestazioni e crea le fonti richieste insieme alle intestazioni originali.

#! /usr/bin/env ruby

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

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

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


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

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

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

#endif
EOS

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

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

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

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

L'uso di espressioni regolari rende questo "parser" piuttosto fragile, potrebbe non essere in grado di gestire le tue intestazioni specifiche con grazia.

Supponiamo che tu abbia un'intestazione toto / ah, contenente le definizioni per le enumerazioni MyEnum e MyEnum2. Lo script creerà:

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

Soluzioni più robuste sarebbero:

  • Compila tutte le fonti che definiscono le enumerazioni e le relative operazioni da un'altra fonte. Ciò significa che definirai le tue enumerazioni in un file XML / YML / qualsiasi cosa che sia molto più facile da analizzare rispetto a C / C ++.
  • Usa un vero compilatore come suggerito da Avdi.
  • Usa macro del preprocessore con o senza modelli.

0

Questo è praticamente l'unico modo in cui può essere fatto (potrebbe funzionare anche un array di stringhe).

Il problema è che, una volta compilato un programma C, il valore binario di enum è tutto ciò che viene utilizzato e il nome è sparito.


0

Ecco un programma CLI che ho scritto per convertire facilmente enumerazioni in stringhe. È facile da usare e ci vogliono circa 5 secondi per farlo (compreso il tempo di cd nella directory contenente il programma, quindi eseguirlo, passando ad esso il file contenente l'enum).

Scarica qui: http://www.mediafire.com/?nttignoozzz

Argomento di discussione su di esso qui: http://cboard.cprogramming.com/projects-job-recruitment/127488-free-program-im-sharing-convertenumtostrings.html

Eseguire il programma con l'argomento "--help" per ottenere una descrizione di come usarlo.


Potresti per favore metterlo in un repository da qualche parte (github, google code o bitbucket) e pubblicare il link qui, invece di mediafire? Aiuterei le persone che vogliono capirlo :)
Edu Felipe

0

Non molto tempo fa ho fatto qualche trucco per avere enum correttamente visualizzati in QComboBox e per avere la definizione di enum e rappresentazioni di stringa come un'unica istruzione

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

Ora puoi enumeration::enum_singleton<your_enum>::instance()convertire le enumerazioni in stringhe. Se sostituisci kv_storage_tcon boost::bimap, sarai anche in grado di eseguire la conversione all'indietro. La classe base comune per il convertitore è stata introdotta per memorizzarla nell'oggetto Qt, perché gli oggetti Qt non possono essere modelli

Aspetto precedente


0

Come variante, usa la libreria semplice> http://codeproject.com/Articles/42035/Enum-to-String-and-Vice-Versa-in-C

Nel codice

#include <EnumString.h>

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

aggiungi linee

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

Funziona bene, se i valori in enum non sono pubblici .

Utilizzo di esempio

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

e viceversa

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

0

Ecco un tentativo di ottenere gli operatori di flusso << e >> su enum automaticamente con un solo comando macro di una riga ...

definizioni:

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

Uso:

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

Non sono sicuro dei limiti di questo schema però ... i commenti sono i benvenuti!


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

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

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

int main()
{

init()
    IDMAP(1)
    IDMAP(2)
    IDMAP(3)
    IDMAP(4)
    IDMAP(5);
std::cout<<enToStr[1];
std::cout<<enToStr[2];
std::cout<<enToStr[3];
std::cout<<enToStr[4];
std::cout<<enToStr[5];
}

2
Spiega gentilmente perché questa è la risposta.
Alok
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.