C’è un modo semplice per convertire enume C ++ in stringa?

Supponiamo di avere alcune enumerazioni nominate:

enum MyEnum { FOO, BAR = 0x50 }; 

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

 char* enum_to_string(MyEnum t); 

E un’implementazione con qualcosa di simile:

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

Il gotcha è in realtà con enumerazioni tipizzate e enum senza stile C. Qualcuno sa qualcosa per questo?

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

Potresti voler controllare GCCXML .

L’esecuzione di GCCXML sul tuo codice di esempio produce:

          

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

Le macro X sono la soluzione migliore. Esempio:

 #include  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 ansible modificare leggermente la stringa.

 #define X(a, b) a, #define X(a, b) b, X(Red, "red") X(Green, "green") // etc. 

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

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 è ansible utilizzare la matrice in luoghi in cui si desidera un valore leggibile dall’uomo, ad es

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

Potresti sperimentare un po 'con l'operatore di stringa (vedi # nel riferimento al preprocessore) che farà ciò che vuoi, in alcune circostanze, ad esempio:

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

stamperà "rosso" su stdout. Sfortunatamente non funzionerà con una variabile (dato che otterrai il nome della variabile stampata)

Ho una macro incredibilmente semplice da usare che fa questo in modo completamente ASCIUTTO. Comprende macro variadiche e alcune semplici magie di parsing. 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 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(value)]; \ return os;} 

Per usare questo nel tuo codice, fai semplicemente:

 AWESOME_MAKE_ENUM(Animal, DOG, CAT, HORSE ); 

QT è in grado di estrarre quello (grazie al compilatore del meta object): link

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

Questa implementazione non richiede alcuna modifica al codice che definisce le costanti, che possono essere enumerazioni o #define s o qualsiasi altra cosa che si dedica a un intero: nel mio caso avevo simboli definiti in termini di altri simboli. Funziona bene anche con valori sparsi. Permette persino più nomi per lo stesso valore, restituendo sempre il primo. L’unico svantaggio è che richiede di creare una tabella delle costanti, che potrebbero non essere aggiornate, ad esempio se ne aggiungono di nuove.

 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 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 funzione IdToName si basa su std::lower_bound per eseguire ricerche rapide, che richiedono la classificazione della tabella. Se le prime due voci nella tabella sono fuori servizio, la funzione lo 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 dichiarazione di switch grandi dimensioni.

 #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; } } 
 #define stringify( name ) # name enum MyEnum { ENUMVAL1 }; ...stuff... stringify(EnumName::ENUMVAL1); // Returns MyEnum::ENUMVAL1 

Ulteriori discussioni su questo metodo

Trucchi della direttiva preprocessore per i nuovi arrivati

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

nel file myenummap.h:

 #include  #include  enum test{ one, two, three, five=5, six, seven }; struct mymap : std::map { 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 container STL ed è un po 'più ordinato:

 #include  #include  #include  #include  //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 mapping; auto m = [](test const& e,std::string const& s){return mapping(static_cast(e),s);}; std::vector 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(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) ) << "]"< 
 #include  #include  #include  #include  #include  #include  #define SMART_ENUM(EnumName, ...) \ class EnumName \ { \ private: \ static std::map nameMap; \ public: \ enum {__VA_ARGS__}; \ private: \ static std::map initMap() \ { \ using namespace std; \ \ int val = 0; \ string buf_1, buf_2, str = #__VA_ARGS__; \ replace(str.begin(), str.end(), '=', ' '); \ stringstream stream(str); \ vector strings; \ while (getline(stream, buf_1, ',')) \ strings.push_back(buf_1); \ map tmp; \ for(vector::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 \ EnumName::nameMap = EnumName::initMap(); 

Uso:

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

Questo può essere fatto in C ++ 11

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

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.

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

  • MyEnum fromString(const string&)
  • compila con VisualStudio 2012
  • l’enum è un tipo POD effettivo (non solo dichiarazioni const), quindi è ansible assegnarlo a una variabile.
  • aggiunta funzione “intervallo” C ++ (in forma di vettore) per consentire “foreach” iterazione 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& tmp) \ { \ using namespace std; \ \ int val = 0; \ string buf_1, buf_2, str = #__VA_ARGS__; \ replace(str.begin(), str.end(), '=', ' '); \ stringstream stream(str); \ vector strings; \ while (getline(stream, buf_1, ',')) \ strings.push_back(buf_1); \ for(vector::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& 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& nameMap() { \ static std::map nameMap0; \ if (nameMap0.size() ==0) initMap(nameMap0); \ return nameMap0; \ } \ static std::vector allValues() { \ std::vector x{ __VA_ARGS__ }; \ return x; \ } \ bool operator<(const EnumName a) const { return (int)*this < (int)a; } \ }; 

Tieni presente che la conversione in stringa è veloce, mentre la conversione da stringa è una ricerca lineare lenta. Ma le stringhe sono comunque molto costose (e il file associato IO), non ho sentito la necessità di ottimizzare o utilizzare una bimap.

La soluzione macro di Suma è carina. Non è necessario avere due macro diverse, però. C ++ includerà felicemente un’intestazione due volte. Basta lasciare fuori la guardia di inclusione.

Quindi avresti un foobar.h che definisca giusto

 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_ARGUMENT s. Nel primo round, espande ENUM come DECLARE_ENUM di Suma; nel secondo turno ENUM funziona come DEFINE_ENUM .

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

Si noti che la funzione di conversione dovrebbe idealmente restituire un const char *.

Se puoi permetterti di inserire le tue enumerazioni nei loro file di intestazione separati, potresti forse fare qualcosa del genere con i 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 del preprocessore) che ti consente di definire il tuo enum in un singolo punto del tuo codice. Il tuo codice non è quindi sobject a errori dovuti a modifiche all’enum. La tua definizione enum e la funzione di conversione saranno sempre sincronizzate. Tuttavia, ripeto, questo è brutto 🙂

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

  • Può generarli per l’enumerazione che non definisco (es .: enumerazione della piattaforma OS)
  • Può incorporare il controllo di intervallo nella class wrapper
  • Può fare una formattazione “più intelligente” con enumerazione dei bit bit

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

Ecco un esempio di enum della mia base di codice, contiene tutto il codice framework che implementa macro e modelli, ma puoi ottenere 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 è di usare EHelpLocation, si usa SEHelpLocation; tutto funziona allo stesso modo, ma si ottiene il controllo dell’intervallo e un metodo ‘Format ()’ sulla variabile enum stessa. Se è necessario formattare un valore autonomo, è ansible utilizzare CEnumFormatter_EHelpLocation :: FormatEnum (…).

Spero che questo sia utile. Mi rendo conto che anche questo non affronta la domanda iniziale su uno script per generare effettivamente l’altra class, ma spero che la struttura aiuti qualcuno a cercare di risolvere lo stesso problema, o scrivere un simile script.

Ecco una soluzione a un file (basata su una risposta elegante di @Marcin:

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

Questa è stata la mia soluzione con BOOST:

 #include  #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  \ 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("reload_log"); QString serviceReloadStr = enumToStr(reload_log); 

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

Quando ho bisogno di questo, di solito:

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

Il seguente script ruby ​​tenta di analizzare le intestazioni e crea le sorgenti richieste accanto 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” abbastanza fragile, potrebbe non essere in grado di gestire le intestazioni specifiche con garbo.

Diciamo che hai un’intestazione toto / ah, contenente le definizioni per enumerare MyEnum e MyEnum2. Lo script costruirà:

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

Soluzioni più solide potrebbero essere:

  • Costruisci tutte le fonti che definiscono le enumerazioni e le loro operazioni da un’altra fonte. Ciò significa che definirai le tue enumerazioni in un file XML / YML / qualunque sia più facile da analizzare rispetto a C / C ++.
  • Utilizzare un compilatore reale come suggerito da Avdi.
  • Utilizza macro di preprocessore con o senza modelli.

È un software inedito, ma sembra che BOOST_ENUM di Frank Laub sia adatto al progetto. La parte che mi piace è che puoi definire un enum nell’ambito di una class che la maggior parte delle enumerazioni basate su Macro di solito non ti lasciano 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 alcun sviluppo dal 2006, quindi non lo faccio sapere come si compila bene con le nuove versioni di Boost. Guarda sotto libs / test per un esempio di utilizzo.

Questo è praticamente l’unico modo per farlo (anche una serie di stringhe potrebbe funzionare).

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

Ecco un programma CLI che ho scritto per convertire facilmente enumerazioni in stringhe. È facile da usare e impiega circa 5 secondi per completarlo (incluso il tempo per effettuare il cd nella directory contenente il programma, quindi eseguirlo, passando ad esso il file contenente l’enum).

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

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

Esegui il programma con l’argomento “–help” per ottenere una descrizione su come usarlo.

Non molto tempo fa ho fatto qualche trucco per avere le enumerazioni correttamente visualizzate in QComboBox e per avere una definizione di enum e rappresentazioni di stringa come una dichiarazione

 #pragma once #include  namespace enumeration { struct enumerator_base : boost::noncopyable { typedef boost::unordered_map 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 struct enumerator; template 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\ : enum_singleton< enumerator >\ { \ 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 hai enumeration::enum_singleton::instance() grado di convertire enum in stringhe. Se sostituisci kv_storage_t con boost::bimap , sarai anche in grado di fare la conversione all’indietro. La class base comune per il convertitore è stata introdotta per memorizzarla nell’object Qt, poiché gli oggetti Qt non potevano essere modelli

Aspetto precedente

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

Nel codice

 #include  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 .

Esempio di utilizzo

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

e viceversa

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

Ecco un tentativo di ottenere << e >> operatori di streaming su enum automaticamente con un comando macro di una sola linea …

definizioni:

 #include  #include  #include  #include  #include  #include  #include  #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 enumStr(name##Str, name##Str + len); \ const std::vector::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; } 

Not sure about the limitations of this scheme though... comments are welcome!

 #include  #include  #define IDMAP(x) (x,#x) std::map 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< 

Check this post:

Class implementation of C++ Enums

it contains class implementation of c++ enum.

I want to post this in case someone finds it useful.

In my case, I simply need to generate ToString() and FromString() functions for a single C++11 enum from a single .hpp file.

I wrote a python script that parses the header file containing the enum items and generates the functions in a new .cpp file.

You can add this script in CMakeLists.txt with execute_process , or as a pre-build event in Visual Studio. The .cpp file will be automatically generated, without the need to manually update it each time a new enum item is added.

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

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

Well, yet another option. A typical use case is where you need constant for the HTTP verbs as well as using is string version values.

The example:

 int main () { VERB a = VERB::GET; VERB b = VERB::GET; VERB c = VERB::POST; VERB d = VERB::PUT; VERB e = VERB::DELETE; std::cout << a.toString() << std::endl; std::cout << a << std::endl; if ( a == VERB::GET ) { std::cout << "yes" << std::endl; } if ( a == b ) { std::cout << "yes" << std::endl; } if ( a != c ) { std::cout << "no" << std::endl; } } 

The VERB class:

 // ----------------------------------------------------------- // ----------------------------------------------------------- class VERB { private: // private constants enum Verb {GET_=0, POST_, PUT_, DELETE_}; // private string values static const std::string theStrings[]; // private value const Verb value; const std::string text; // private constructor VERB (Verb v) : value(v), text (theStrings[v]) { // std::cout << " constructor \n"; } public: operator const char * () const { return text.c_str(); } operator const std::string () const { return text; } const std::string toString () const { return text; } bool operator == (const VERB & other) const { return (*this).value == other.value; } bool operator != (const VERB & other) const { return ! ( (*this) == other); } // --- static const VERB GET; static const VERB POST; static const VERB PUT; static const VERB DELETE; }; const std::string VERB::theStrings[] = {"GET", "POST", "PUT", "DELETE"}; const VERB VERB::GET = VERB ( VERB::Verb::GET_ ); const VERB VERB::POST = VERB ( VERB::Verb::POST_ ); const VERB VERB::PUT = VERB ( VERB::Verb::PUT_ ); const VERB VERB::DELETE = VERB ( VERB::Verb::DELETE_ ); // end of file