2012-02-05 34 views

Respuesta

17

Mientras que lo que comúnmente se realiza a través de los interruptores, prefiero matrices:

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

El tamaño de la matriz explícita tiene el beneficio de generar un tiempo de compilación de error si el tamaño del cambio de enumeración y que se olvide de añadir el cadena apropiada.

Alternativamente, hay Boost.Enum en la bóveda de Boost. La biblioteca no se ha lanzado oficialmente, pero es bastante estable y proporciona el número que desee. Sin embargo, no se lo recomendaría a un novato.

+3

No genera un error si olvida * agregar * cadenas ... –

+1

@MatthieuM. Cierto. La única manera confiable de construir algo como esto probablemente sean las macros. – pmr

+0

Me temo que sí. Siempre pensé que era una lástima, habría apreciado que el compilador construyera la función en el lateral. Una de las formas que encontré fue el uso de macros, aunque esto se basa en la función más o menos. La otra consistía en utilizar pruebas para asegurarse de que la traducción funcionaba para los tipos anteriores (al menos) y luego confiar en el desarrollador para * pensar * :) –

8

Eso es inherentemente imposible.

A C++ enum es simplemente un conjunto de números con nombres de tiempo de compilación.
En tiempo de ejecución, son indistinguibles de los números normales.

Necesita escribir una declaración switch que devuelve una cadena.

+1

cómo funciona en C# .... ¿reflexión? – CodingHero

+0

@CodingQuant: Sí. Puedes ver el reflejo en el origen de la clase 'Enum'. – SLaks

0

Puede almacenar los nombres en una matriz de cadenas, indexadas por los valores enum.

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

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

entonces usted puede imprimir: "Invalid colour '" + names[colour] + "' selected."

Pero este enfoque puede no ser muy útil si no se definen los valores enum secuencialmente. En ese caso, este enfoque perderá memoria. Escribir una función con un switch sobre el valor enum sería útil, como ha mencionado Alexander Gessler. Otra alternativa puede ser el map de STL.

1

Tienes que hacerlo de forma manual, es decir

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

Una tabla de búsqueda también sería posible. También he visto a gente usando scripts personalizados para generar tales cosas sobre su código fuente.

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

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

Uso ColorMap[c] para conseguir la representación de cadena:

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

Sin embargo, si los valores de enumeración no son continuas, entonces usted puede utilizar std::map en cambio, como:

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

En mi opinión, la mejor solución ya que este enfoque también se puede utilizar con enums que contiene huecos. P.ej. Rojo = 0x01, verde, azul = 0x10, amarillo. – Anonymous

0

Como @FlopCoder dijo:

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

Esto, por supuesto, solo funcionará si su enumeración comienza en 0 y es continua.
@ La forma de Nawaz es más C++ con estilo embargo.

+0

Eche un vistazo a mi restricción de tamaño. Esto hace que el idioma sea más agradable. – pmr

+0

De hecho, mi amigo. – Petruza

14

¿Qué tal un poco de magia con macros:

#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í, entiendo que esto es feo y será mejor que no hago esas cosas

+0

¿Ugly? ¡Esto es fantástico! –

+0

Estoy bastante seguro de que esto se puede hacer mucho más limpiamente con los paquetes de parámetros de C++ 11, por lo que no tendrás ningún trabajo en tiempo de ejecución. – einpoklum

3

me gusta mucho el enfoque macro de @ Lol4t0 .

me prolongado que sea capaz de convertir una enumeración de una cadena demasiado:

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

Uso:

ENUM(MyEnum, Value1, Value2) 

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

No soy experto en C++, así que vamos a saber lo que piensa o cómo hacerlo mejor