2012-02-05 12 views
6

Come faccio a ToString() un enum in C++? In Java e C# chiamerei semplicemente ToString.Come faccio a ToString() un enum in C++?

enum Colours 
{ 
    Red =0, 
    Green=1, 
    Blue=2 
}; 

Ho bisogno di creare una stringa del tipo: "Colore non valido" "+ colore +" 'selezionato. "

risposta

17

Mentre questo è comunemente fatto attraverso gli interruttori, preferisco gli array:

#include <iostream> 

namespace foo { 
    enum Colors { BLUE = 0, RED, GREEN, SIZE_OF_ENUM }; 
    static const char* ColorNames[] = { "blue", "red", "green" }; 

    // statically check that the size of ColorNames fits the number of Colors 
    static_assert(sizeof(foo::ColorNames)/sizeof(char*) == foo::SIZE_OF_ENUM 
    , "sizes dont match"); 
} // foo 

int main() 
{ 
    std::cout << foo::ColorNames[foo::BLUE] << std::endl; 
    return 0; 
} 

La dimensione esplicita array ha il vantaggio di generare un momento della compilazione errore opportuno la dimensione del cambiamento enum e vi dimenticate di aggiungere il stringa appropriata.

In alternativa, c'è Boost.Enum nella volta Boost. La libreria non è stata rilasciata ufficialmente ma è abbastanza stabile e fornisce ciò che si desidera . Non lo consiglierei a un principiante però.

+3

Non genera un errore se si dimentica di * aggiungere * stringhe ... –

+1

@MatthieuM. Vero. L'unico modo affidabile per creare qualcosa di simile sono probabilmente le macro. – pmr

+0

Temo di sì. Ho sempre pensato che fosse un peccato, avrei apprezzato il compilatore che costruiva la funzione sul lato. Un modo che ho trovato è stato l'utilizzo di macro, anche se questo è in linea la funzione più o meno. L'altro era usare i test per assicurarsi che la traduzione funzionasse per i tipi precedenti (almeno) e poi affidarsi allo sviluppatore per * think * :) –

8

Questo è intrinsecamente impossibile.

Un enumo di C++ è solo un insieme di numeri con nomi in fase di compilazione.
In fase di runtime, sono indistinguibili dai numeri ordinari.

È necessario scrivere una dichiarazione switch che restituisce una stringa.

+1

come funziona in C# .... riflessione? – CodingHero

+0

@CodingQuant: Sì. Puoi vedere il riflesso nella fonte della classe 'Enum'. – SLaks

0

È possibile memorizzare i nomi in una serie di stringhe, indicizzate dai valori enum.

enum Colours 
{ 
    Red =0, 
    Green=1, 
    Blue=2 
}; 

char* names[3] = {"Red", "Green", "Blue"}; 

quindi è possibile stampare: "Invalid colour '" + names[colour] + "' selected."

Ma questo approccio non può essere molto utile se non si definiscono i valori enum in sequenza. In tal caso questo approccio sprecherà memoria. Scrivere una funzione con un valore enum sul valore enum sarebbe utile, come ha menzionato Alexander Gessler. Un'altra alternativa potrebbe essere map da STL.

1

Devi farlo manualmente, vale a dire

const char* ToString(Colours co) { 
    switch(co) { 
     case Red: 
      return "Red"; 
     // ... 
    } 
} 

una tabella di ricerca sarebbe anche possibile. Ho anche visto persone che usano script personalizzati per generare tali cose oltre al loro codice sorgente.

3
enum Color 
{ 
    Red =0, 
    Green=1, 
    Blue=2 
}; 

std::string ColorMap[] = { "Red", "Green","Blue" }; 

Usa ColorMap[c] per ottenere la rappresentazione di stringa:

std::string msg = "Invalid colour '" + ColorMap[c] + "' selected."; 

Tuttavia, se i valori di enum non sono continui, quindi è possibile utilizzare std::map invece come:

enum Color 
{ 
    Red = 0x1, 
    Green = 0x2, 
    Blue = 0x4, 
    Black = 0x8, 
}; 

//C++11 only, as it uses std::initializer_list 
std::map<Color, std::string> ColorMap = { 
    {Red, "Red"}, 
    {Green, "Green"}, 
    {Blue, "Blue"}, 
    {Black, "Black"} 
}; 

//same as before! 
std::string msg = "Invalid colour '" + ColorMap[c] + "' selected."; 
+1

Secondo me la soluzione migliore poiché questo approccio può essere utilizzato anche con le enumerazioni che contengono spazi vuoti. Per esempio. Rosso = 0x01, Verde, Blu = 0x10, Giallo. – Anonymous

0

Come @FlopCoder ha detto:

enum Colours 
{ 
    Red =0, 
    Green=1, 
    Blue=2 
}; 
char* ColourNames[] = { "Red", "Green", "Blue" }; 
int colour = Green; 
printf("Invalid colour '%s' selected.", ColourNames[ colour ]); 

Questo ovviamente funzionerà solo se il tuo enum parte da 0 ed è continuo.
@ modo di Nawaz è più C++ elegante però.

+0

Dai un'occhiata al mio vincolo di dimensioni. Questo rende l'idioma un po 'più bello. – pmr

+0

Anzi, amico mio. – Petruza

14

Che ne dite di un po 'di magia con le macro:

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


// http://stackoverflow.com/questions/236129/how-to-split-a-string-in-c 
std::vector<std::string> split(const std::string &text, char sep) { 
    std::vector<std::string> tokens; 
    int start = 0, end = 0; 
    while ((end = text.find(sep, start)) != std::string::npos) { 
     tokens.push_back(text.substr(start, end - start)); 
     start = end + 1; 
    } 
    tokens.push_back(text.substr(start)); 
    return tokens; 
} 

#define ENUM(name, ...)\ 
enum name \ 
{\ 
__VA_ARGS__\ 
};\ 
std::vector<std::string> name##Map = split(#__VA_ARGS__, ',');\ 
    std::string toString(const name v) { return name##Map.at(v);} 


ENUM(Color, Red,Green,Blue) 


int main(int c, char**v) 
{ 
    std::cout << toString(Red) << toString(Blue); 
    return 0;//a.exec(); 
} 

Sì, ho capito che questo è brutto e è meglio che non fai queste cose

+0

Brutto? È fantastico! –

+0

Sono abbastanza sicuro che questo può essere fatto in modo molto più pulito con i pacchetti di parametri C++ 11 - quindi non avrete alcun lavoro in fase di esecuzione. – einpoklum

3

mi piace molto l'approccio macro di @ Lol4t0 .

ho esteso per essere in grado di convertire un enum da una stringa troppo:

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

// http://stackoverflow.com/questions/236129/how-to-split-a-string-in-c 
std::vector<std::string> split(const std::string &text, char sep) { 
    std::vector<std::string> tokens; 
    int start = 0, end = 0; 
    while ((end = text.find(sep, start)) != std::string::npos) { 
     tokens.push_back(text.substr(start, end - start)); 
     start = end + 1; 
    } 
    tokens.push_back(text.substr(start)); 
    return tokens; 
} 

#define ENUM(name, ...)\ 
    enum name\ 
    {\ 
     __VA_ARGS__\ 
    };\ 
    static const int name##Size = (sizeof((int[]){__VA_ARGS__})/sizeof(int));\ 
    static const vector<string> name##ToStringMap = split(#__VA_ARGS__, ',');\ 
    const string name##ToString(const name value)\ 
    {\ 
     return name##ToStringMap.at(value);\ 
    };\ 
    map<string, name> name##ToFromStringMap(...)\ 
    {\ 
     map<string, name> m;\ 
     name args[name##Size] = { __VA_ARGS__ };\ 
     \ 
     int i;\ 
     for(i = 0; i < name##Size; ++i)\ 
     {\ 
      m[name##ToString(args[i])] = args[i];\ 
     }\ 
     return m;\ 
    };\ 
    static map<string, name> name##FromStringMap = name##ToFromStringMap(__VA_ARGS__);\ 
    const name name##FromString(const string value, const name defaultValue)\ 
    {\ 
     if(name##FromStringMap.count(value) == 0)\ 
     {\ 
      return defaultValue;\ 
     }\ 
     return name##FromStringMap[value];\ 
    }; 

Usage:

ENUM(MyEnum, Value1, Value2) 

void main() 
{ 
    string valueName = MyEnumToString(MyEnum::Value2); 
    MyEnum value = MyEnumFromString(valueName, MyEnum::Value1); 
} 

io non sono un C++ esperto, quindi fatemi sapere cosa ne pensate o come fare meglio

Problemi correlati