2008-10-14 6 views
104

Supongamos que tenemos algunas enumeraciones con nombre:¿Hay una forma simple de convertir C++ enum en una cadena?

enum MyEnum { 
     FOO, 
     BAR = 0x50 
}; 

Lo busqué en Google es un guión (cualquier idioma) que escanea todos los encabezados en mi proyecto y genera un encabezado con una función por enumeración.

char* enum_to_string(MyEnum t); 

Y una aplicación con algo como esto:

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

El Gotcha es realmente con enumeraciones typedefed, y enumeraciones de estilo C sin nombre. ¿Alguien sabe algo de esto?

EDITAR: La solución no debe modificar mi fuente, a excepción de las funciones generadas. Las enumeraciones están en una API, por lo que utilizar las soluciones propuestas hasta ahora no es una opción.

+0

posible duplicado de [Forma fácil de usar las variables de los tipos enum como cadena en C?] (Http://stackoverflow.com/questions/147267/easy-way-to-use-variables-of-enum-types- as-string-in-c) – karlphillip

+0

Respuesta sobre la fábrica basada en macro se trasladó a http://stackoverflow.com/questions/147267/easy-way-to-use-variables-of-enum-types-as-string-in- C# 202511 - después de actualizar la pregunta, ya no es relevante aquí. – Suma

Respuesta

45

Es posible que desee comprobar hacia fuera GCCXML.

Correr GCCXML en su código de ejemplo 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> 

Se puede utilizar cualquier idioma que prefiera para sacar las etiquetas de enumeración y EnumValue y generar el código deseado.

+0

¡Excelente! Funcionó como un encanto con un simple script de python. Gracias. –

+6

+1, GCCXML se ve muy bien! (Aunque casi me apresuré a que inicialmente malinterpretara esto como una sugerencia para usar la sintaxis XML detallada antes descrita para codificar tu enum, ¡una solución que apesta a sobreinnovación!) –

+1

¿Algún cambio puede publicar el script de python? – phillipwei

0

Esa es prácticamente la única forma en que se puede hacer (una serie de cuerdas podría funcionar también).

El problema es que, una vez que se compila un programa C, el valor binario de la enumeración es todo lo que se utiliza, y el nombre desaparece.

29

Lo que suelo hacer es crear una matriz C con los nombres en el mismo orden y posición que los valores enum.

por ejemplo.

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

entonces se puede usar la matriz en los lugares donde se desea un valor legible por humanos, por ejemplo

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

Usted podría experimentar un poco con el operador stringizing (ver # en su referencia preprocesador) que hará lo que quiera, en algunas circunstancias, por ejemplo:

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

imprimirá "rojo" a la salida estándar. Desafortunadamente no funcionará para una variable (como se imprimirá el nombre de la variable)

+0

La última advertencia (no funcionará para una variable) es un gran inconveniente, pero +1 de todos modos. – chappjc

+2

Funciona solo si no configura valores numéricos especiales para entradas enum. – kyb

1

Un problema con la respuesta 0 es que los valores binarios enum no comienzan necesariamente en 0 y no son necesariamente contiguos.

Cuando necesito esto, por lo general:

  • tire de la definición de enumeración en mi fuente
  • editarlo para obtener sólo los nombres
  • hacer una macro para cambiar el nombre de la cláusula caso en el la pregunta, aunque típicamente en una línea: caso foo: return "foo";
  • añadir el interruptor, por defecto y otra sintaxis para que sea legal
68

X-macros son la mejor solución. Ejemplo:

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

colores.def:

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

Sin embargo, por lo general prefieren el siguiente método, por lo que es posible ajustar la cadena un poco.

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

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

ingenioso, aunque no me gusta el archivo adicional –

+0

+1, pero es probable que ligue con Intellisense, y cosas por el estilo ... –

+2

Solo asegúrate de que tu proceso de compilación no antependa a #pragma (una vez) antes de incluir archivo ... – xtofl

3

Suma's macro solution es agradable. Sin embargo, no necesita tener dos macro diferentes. C++ felizmente incluirá un encabezado dos veces. Solo deja afuera el protector de inclusión.

por lo que tendría un foobar.h definir simplemente

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

y que incluiría así:

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

enumfactory.h hará #include ENUMFACTORY_ARGUMENT 2 s. En la primera ronda, se expande ENUM como el DECLARE_ENUM de Suma; en la segunda ronda ENUM funciona como DEFINE_ENUM.

Puedes incluir enumfactory.h varias veces, también, el tiempo que se pasa en diferentes # define para ENUMFACTORY_ARGUMENT de

+0

parece que suma movió la respuesta [aquí] (https://stackoverflow.com/questions/147267/easy-way-to-use -variables-of-enum-types-as-string-in-C# 202511). Es posible que desee incluir el enlace en su respuesta. Solo encontré el comentario por casualidad y sumas sin respuesta, este es bastante inútil – user463035818

2

Tenga en cuenta que su función de conversión idealmente debería ser devolver const char *.

Si usted puede permitirse el lujo de poner las enumeraciones en sus ficheros de cabeceras, que tal vez podría hacer algo como esto con macros (oh, esto va a ser feo):

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

Dónde enum_def.h tiene:

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

Y enum_conv.h tiene:

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

Y, por último, tiene colour.h:

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

Y puede utilizar la función de conversión como:

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

Ésta es feo, pero es la única manera (a nivel de preprocesador) que permite definir su enumeración acaba en un solo lugar en tu codigo. Por lo tanto, su código no es propenso a errores debido a modificaciones en la enumeración. Tu definición de enum y la función de conversión siempre estarán sincronizadas. Sin embargo, repito, esto es feo :)

1

El siguiente script de ruby ​​intenta analizar los encabezados y construye las fuentes necesarias junto con los encabezados originales.

#! /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)) 
    } 
    } 
} 

Uso de expresiones regulares hace que este "analizador" muy frágil, que puede no ser capaz de manejar sus cabeceras específicas con gracia.

Digamos que tiene un encabezado toto/a.h, que contiene definiciones para enumeraciones MyEnum y MyEnum2. El script construir:

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

soluciones más robustas serían:

  • Construir todas las fuentes que definen las enumeraciones y sus operaciones de otra fuente. Esto significa que definirá sus enumeraciones en un archivo XML/YML/lo que es mucho más fácil de analizar que C/C++.
  • Use un compilador real como el sugerido por Avdi.
  • Use macros de preprocesador con o sin plantillas.
2

Otra respuesta: en algunos contextos, tiene sentido para definir su enumeración en un formato no código, como un CSV, YAML, o un archivo XML, y luego generar tanto el ++ código de enumeración C y la cadena de código de la definición. Este enfoque puede o no ser práctico en su aplicación, pero es algo a tener en cuenta.

8

QT es capaz de tirar de la de (gracias al compilador de meta objeto): link

2

lo hago con enumeración clases contenedoras separadas de lado a lado que se generan con macros. Hay varias ventajas:

  • los puede generar para las enumeraciones no me defino (por ejemplo: las enumeraciones de cabecera plataforma de sistema operativo)
  • puede incorporar gama de registrarse en la clase contenedora
  • puede hacer "más inteligente" con el formato Enum de campo de bit

La desventaja, por supuesto, es que necesito duplicar los valores enum en las clases de formateador, y no tengo ningún script para generarlos. Aparte de eso, parece funcionar bastante bien.

He aquí un ejemplo de una enumeración de mi base de código, sans todo el código marco que implementa las macros y plantillas, pero se puede conseguir la 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; 

La idea es, entonces, en lugar de utilizar EHelpLocation, se use SEHelpLocation; todo funciona igual, pero obtienes un control de rango y un método 'Format()' en la variable enum. Si necesita formatear un valor independiente, puede usar CEnumFormatter_EHelpLocation :: FormatEnum (...).

Espero que esto sea útil. Me doy cuenta de que esto tampoco aborda la pregunta original sobre un guión para generar realmente la otra clase, pero espero que la estructura ayude a alguien a tratar de resolver el mismo problema, o escriba ese guión.

38

@hydroo: Sin el archivo adicional:

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

Me encanta esta solución. Sin embargo, sería más claro si SOME_UNION y MAKE_UNION se llamaran SOME_ENUM y MAKE_ENUM. –

+0

Esta es una gran solución. Tengo el administrador de recursos C++ más sostenible con el que he tratado. – DCurro

+0

Debo agradecerle por esta sencilla solución :-). Lo modifiqué un poco, para que el 'MetaSyntacticVariableNames []' sea parte de una declaración de clase, al hacer un método' static const char * getNameByEnum (MetaSyntacticVariable e) {/ * código para devolver la cadena estática * /} ' – DeckerDK

5

interesante ver el número de maneras. Aquí hay uno que utilicé hace mucho tiempo:

en el archivo 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(){}; 
}; 

en main.cpp

#include "myenummap.h" 

... 
mymap nummap; 
std::cout<< nummap[ one ] << std::endl; 

No es constante, pero su conveniente.

Aquí hay otra manera que usa las características de C++ 11. Esta es constante, no hereda un contenedor STL y es un poco más ordenado:

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

Heredar un contenedor stl no es una buena idea –

+1

Es perfectamente legal. Lo hago todo el tiempo. –

+0

Buena solución. Esto es C++, entonces usar stl map está bien. –

1

Es un software inédito pero parece BOOST_ENUM de Frank Laub podría caber la cuenta. La parte que me gusta es que puedes definir una enumeración dentro del alcance de una clase que la mayoría de las enumeraciones basadas en macros normalmente no te dejan hacer. Está ubicado en Boost Vault en: http://www.boostpro.com/vault/index.php?action=downloadfile&filename=enum_rev4.6.zip&directory=& No se ha visto ningún desarrollo desde 2006, así que no sé qué tan bien se compila con las nuevas versiones de Boost. Busque en libs/test un ejemplo de uso.

0

Aquí hay un programa CLI que escribí para convertir fácilmente enumeraciones en cadenas. Es fácil de usar y demora unos 5 segundos en completarse (incluido el tiempo para crear un CD en el directorio que contiene el programa, luego ejecutarlo y pasarle el archivo que contiene la enumeración).

Descargar aquí: http://www.mediafire.com/?nttignoozzz

tema de debate sobre ello aquí: http://cboard.cprogramming.com/projects-job-recruitment/127488-free-program-im-sharing-convertenumtostrings.html

Ejecutar el programa con el argumento "help" para obtener una descripción de cómo usarlo.

+0

¿Podría poner esto en un repositorio en alguna parte (github, google code o bitbucket) y publicar el enlace aquí, en lugar de mediafire? Ayudaría a las personas que quieran entenderlo :) –

+0

Bien, aquí está el enlace de Google Code: http://code.google.com/p/convertenumtostrings/ –

7

Acabo de reinventar esta rueda hoy, y pensé en compartirla.

Esta aplicación hace no requiere ningún cambio en el código que define las constantes, que pueden ser enumeraciones o #define s o cualquier otra cosa que incumbe a un entero - en mi caso yo había símbolos se definen en términos de otros símbolos. También funciona bien con valores dispersos. Incluso permite múltiples nombres para el mismo valor, devolviendo siempre el primero. El único inconveniente es que se requiere que hagas una tabla de las constantes, que pueden quedar desactualizadas a medida que se agregan nuevas, por ejemplo.

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 ejemplo de cómo debe usarlo:

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 función se basa en IdToNamestd::lower_bound hacer búsquedas rápidas, lo que requiere la mesa para ser ordenados. Si las primeras dos entradas de la tabla están desordenadas, la función lo ordenará automáticamente.

Editar: Un comentario me hizo pensar en otra forma de utilizar el mismo principio. Una macro simplifica la generación de una gran declaración switch.Hace

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

Buena solución. Pero para mí preferiría 'switch and case' ya que es simple y fácil de entender. – Deqing

6
#define stringify(name) # name 

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

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

Further discussion on this method

Preprocessor directive tricks for newcomers

+0

Esto realmente debería tener más votos. – AzP

+3

En realidad esto es bastante inútil, ya que el método de stringify está en tiempo de compilación y es bastante literal. Si dices que tienes el tipo de enumeración en cuestión dentro de una variable, intentar codificar la variable simplemente te dará el nombre de la variable, no el nombre del tipo enum. – srcspider

0
No

tanto tiempo que hizo un truco para tener enumeraciones correctamente mostradas en QComboBox y tener definición de enumeración y representaciones de serie como una declaración

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

Ahora tienes enumeration::enum_singleton<your_enum>::instance() capaz de convertir enumeraciones a cadenas. Si reemplaza kv_storage_t con boost::bimap, también podrá realizar la conversión hacia atrás. clase base común para el convertidor se introdujo para almacenarlo en el objeto de Qt, ya que los objetos de Qt no podrían ser las plantillas

Previous appearance

0

Como variante, utilizar lib sencilla>http://codeproject.com/Articles/42035/Enum-to-String-and-Vice-Versa-in-C

En el código

#include <EnumString.h> 

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

líneas add

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

Funciona bien, si los valores en enumeración no son duplicados.

Ejemplo de uso

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

y viceversa

assert(EnumString<FORM>::To(f, str)); 
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

Me gusta su API, pero desafortunadamente, su SmartEnum no crea enum "tipo". No puede hacer 'MyEnum x = MyEnum :: TWO;'. He publicado mi edición de tu clase para apoyar esto. –

0

Aquí es un intento de conseguir < < y >> operadores de flujo de correo num de forma automática, con sólo un comando de una línea macro ...

Definiciones:

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

No está seguro acerca de las limitaciones de este esquema, aunque ... los comentarios son bienvenidos!

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]; 
} 
+1

Explique amablemente por qué esta es la respuesta. – Gogo

9

Tengo una macro increíblemente fácil de usar que hace esto de una manera completamente seca. Se trata de macros variadas y algunos simples análisis de magia. Aquí va:

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

usar esto en su código, basta con hacer:

AWESOME_MAKE_ENUM(Animal, 
    DOG, 
    CAT, 
    HORSE 
); 
+1

Buena idea usando una enumeración fuertemente tipada (clase enum). Aquí hay una demostración: http://cpp.sh/4ife – chappjc

+0

Funciona con enumeraciones/símbolos definidos externamente. Por ejemplo, ¿OS definido o símbolos definidos por la biblioteca con lagunas en la numeración? –

+0

Muy bonito, pero no se compila si se coloca dentro de una clase (no pude entender por qué). – AlwaysLearning

3

Esta es una modificación al @ user3360260 respuesta.Tiene las siguientes características nuevas

  • MyEnum fromString(const string&) apoyo
  • compila con VisualStudio 2012
  • la enumeración es un tipo POD real (no sólo las declaraciones const), por lo que se puede asignar a una variable.
  • añadieron C++ característica de "gama" (en forma de vector) para permitir iteración "foreach" sobre 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; 
} 

Aquí está el código

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

Nota que la conversión a String es una búsqueda rápida, mientras que la conversión de String es una búsqueda lineal lenta. Pero las cadenas son tan caras de todos modos (y el archivo asociado IO), no sentí la necesidad de optimizar o usar un bimap.

+0

Usted y user3360260 tienen una buena solución. ¿Por qué no tener un multimap en su lugar? – Vincent

4

Esto se puede hacer en 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; 
} 
+0

Esto no responde a la pregunta del OP: estaba buscando una forma ** de generar automáticamente ** una función para devolver el nombre de un miembro de la enumeración como una cadena. – Spooky

+0

Gracias sargento !!! – Hans

2

Aquí la solución de un archivo C++ (basado en la elegante respuesta por @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

Esta fue mi solución 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());      \ 
    } 

Para crear enumeración, declaran:

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

Para conversiones:

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

Quiero publicar esto en caso de que alguien lo encuentre útil.

En mi caso, simplemente necesito generar ToString() y FromString() funciones para una sola enum de C++ 11 desde un único archivo .hpp.

Escribí una secuencia de comandos python que analiza el archivo de encabezado que contiene los elementos enum y genera las funciones en un nuevo archivo .cpp.

Puede agregar esta secuencia de comandos en CMakeLists.txt con execute_process, o como un evento de preconstrucción en Visual Studio. El archivo .cpp se generará automáticamente, sin la necesidad de actualizarlo manualmente cada vez que se agrega un nuevo 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) 

Ejemplo:

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

Run python generate_enum_strings.py ErrorCode.hpp

Resultado:

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

Aquí hay un generador en línea: http://th-thielemann.de/tools/cpp-enum-to-string.html –

0

Bueno, otra opción. Un caso de uso típico es donde necesita constante para los verbos HTTP, así como el uso de valores de versión de cadena.

El ejemplo:

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

} 

La clase VERBO:

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

Uso de declaraciones ternarias compuesto puede ser algo elegante para enumeraciones con pocos elementos (de una sola línea). La expresión solo crece de forma aproximadamente lineal en longitud con la cantidad de elementos también.

Aquí es un buen caso de uso:

enum log_level {INFO, WARNING, ERROR}; 
... 
void logger::write(const std::string log, const log_level l) { 
    ... 
    std::string s = (l == INFO) ? "INFO" : 
        (l == WARNING) ? "WARNING" : 
        (l == ERROR) ? "ERROR" : "UNKNOWN"; 
    ... 
} 
... 

Por supuesto, es sólo otro interruptor si el bloque/declaración, pero es una declaración de una sola línea. Y como una cuestión de concisión vs. simplicidad, se encuentra en algún lugar en el medio. Como expresión constante, también se puede convertir fácilmente en una función en línea.

Cuestiones relacionadas