¿Cómo hacer que printf muestre los valores de variables que son de tipo enum? Por ejemplo:
typedef enum {Linux, Apple, Windows} OS_type;
OS_type myOS = Linux;
y lo que necesito es algo como
printenum(OS_type, "My OS is %s", myOS);
que debe mostrar una cadena "Linux", no un número entero.
Supongo que primero tengo que crear una matriz de cadenas indexadas en valores. Pero no sé si esa es la forma más hermosa de hacerlo. ¿Es posible en absoluto?
c++
c
preprocessor
ansi-c
psihodelia
fuente
fuente
Respuestas:
Realmente no hay una manera hermosa de hacer esto. Simplemente configure una matriz de cadenas indexadas por la enumeración.
Si realiza muchos resultados, puede definir un operador << que tome un parámetro enum y realice la búsqueda por usted.
fuente
La solución ingenua, por supuesto, es escribir una función para cada enumeración que realice la conversión a cadena:
Esto, sin embargo, es un desastre de mantenimiento. Con la ayuda de la biblioteca Boost.Preprocessor, que se puede usar con código C y C ++, puede aprovechar fácilmente el preprocesador y dejar que genere esta función por usted. La macro de generación es la siguiente:
La primera macro (comenzando por
X_
) es utilizada internamente por la segunda. La segunda macro primero genera la enumeración, luego genera unaToString
función que toma un objeto de ese tipo y devuelve el nombre del enumerador como una cadena (esta implementación, por razones obvias, requiere que los enumeradores asignen valores únicos).En C ++, podría implementar la
ToString
función como unaoperator<<
sobrecarga, pero creo que es un poco más limpio requerir un "ToString
" explícito para convertir el valor en forma de cadena.Como ejemplo de uso, su
OS_type
enumeración se definiría de la siguiente manera:Si bien la macro se ve al principio como si fuera mucho trabajo, y la definición de
OS_type
apariencia es bastante extraña, recuerde que tiene que escribir la macro una vez, luego puede usarla para cada enumeración. Puede agregarle funcionalidad adicional (por ejemplo, una conversión de forma de cadena a enumeración) sin demasiados problemas, y resuelve completamente el problema de mantenimiento, ya que solo tiene que proporcionar los nombres una vez, cuando invoca la macro.La enumeración se puede usar como si se definiera normalmente:
Los fragmentos de código en esta publicación, comenzando con la
#include <boost/preprocessor.hpp>
línea, se pueden compilar tal como se publicaron para demostrar la solución.Esta solución particular es para C ++, ya que utiliza la sintaxis específica de C ++ (p. Ej., No
typedef enum
) y la sobrecarga de funciones, pero sería sencillo hacer que esto también funcione con C.fuente
(Windows)
y(Windows, 3)
luego reemplazar elBOOST_PP_SEQ_ENUM
con un escrito adecuadamenteBOOST_PP_SEQ_FOR_EACH
. No tengo un ejemplo tan útil, pero puedo escribir uno si lo desea.Este es el bloque de preprocesador
Definición de enumeración
Llamar usando
Tomado de aquí . Cuan genial es eso ? :)
fuente
Úselo
std::map<OS_type, std::string>
y llénelo con enum como clave y la representación de cadena como valores, luego puede hacer esto:fuente
El problema con las enumeraciones de C es que no es un tipo propio, como lo es en C ++. Una enumeración en C es una forma de asignar identificadores a valores integrales. Solo eso. Es por eso que un valor enum es intercambiable con valores enteros.
Como adivinas correctamente, una buena manera es crear una asignación entre el valor de enumeración y una cadena. Por ejemplo:
fuente
enum
son tipos en C. Las constantes de tipo de enumeración integral son de tipoint
y no delenum
tipo a través del cual se definen, es quizás lo que quisiste decir. Pero no veo en absoluto lo que esto tiene que ver con la pregunta.He combinado el James , Howard y de Eder soluciones y ha creado una aplicación más genérica:
El código completo está escrito a continuación (use "DEFINE_ENUM_CLASS_WITH_ToString_METHOD" para definir una enumeración) ( demostración en línea ).
fuente
Este simple ejemplo funcionó para mí. Espero que esto ayude.
fuente
¿Intentaste esto?
La
stringify()
macro se puede usar para convertir cualquier texto de su código en una cadena, pero solo el texto exacto entre paréntesis. No hay desreferenciación de variables ni sustituciones de macros ni ningún otro tipo de cosa realizada.http://www.cplusplus.com/forum/general/2949/
fuente
Aquí hay muchas buenas respuestas, pero pensé que algunas personas podrían encontrar la mía útil. Me gusta porque la interfaz que usas para definir la macro es lo más simple posible. También es útil porque no tiene que incluir ninguna biblioteca adicional: todo viene con C ++ y ni siquiera requiere una versión realmente tardía. Extraje piezas de varios lugares en línea, así que no puedo dar crédito por todo, pero creo que es lo suficientemente único como para garantizar una nueva respuesta.
Primero haga un archivo de encabezado ... llámelo EnumMacros.h o algo así, y ponga esto en él:
Luego, en su programa principal, puede hacer esto ...
Donde la salida sería >> El valor de 'Apple' es: 2 de 4
¡Disfrutar!
fuente
Suponiendo que su enumeración ya está definida, puede crear una matriz de pares:
Ahora, puedes crear un mapa:
Ahora puedes usar el mapa. Si se cambia su enumeración, debe agregar / eliminar pares de pares de matriz []. Creo que es la forma más elegante de obtener una cadena de enumeración en C ++.
fuente
bimap
en caso de que quiera analizar nombres y convertirlos en enumeraciones (por ejemplo, desde un archivo XML).Para C99 hay
P99_DECLARE_ENUM
en P99 que te permite simplemente declararenum
así:y luego se usa
color_getname(A)
para obtener una cadena con el nombre del color.fuente
Aquí está mi código C ++:
fuente
Un poco tarde para la fiesta, pero aquí está mi solución C ++ 11:
fuente
error: ‘hash’ is not a class template
->#include <functional>
Mi preferencia es minimizar tanto la escritura repetitiva como las macros difíciles de comprender y evitar introducir definiciones de macro en el espacio general del compilador.
Entonces, en el archivo de encabezado:
y la implementación de cpp es:
Tenga en cuenta el #undef de la macro tan pronto como hayamos terminado con ella.
fuente
Mi solución, no usar boost:
Y aquí está cómo usarlo
fuente
Otra tarde a la fiesta, usando el preprocesador:
(Solo pongo los números de línea para que sea más fácil hablar de ellos). Las líneas 1-4 son lo que edita para definir los elementos de la enumeración. (Lo he llamado una "macro lista", porque es una macro que hace una lista de cosas. @Lundin me informa que se trata de una técnica bien conocida llamada X-macros).
La línea 7 define la macro interna para completar la declaración de enumeración real en las líneas 8-11. La línea 12 no define la macro interna (solo para silenciar la advertencia del compilador).
La línea 14 define la macro interna para crear una versión de cadena del nombre del elemento enum. Luego, las líneas 15-18 generan una matriz que puede convertir un valor enum a la cadena correspondiente.
Las líneas 21-27 generan una función que convierte una cadena al valor enum, o devuelve NULL si la cadena no coincide con ninguna.
Esto es un poco engorroso en la forma en que maneja el elemento 0. De hecho, he trabajado en eso en el pasado.
Admito que esta técnica molesta a las personas que no quieren pensar que el preprocesador en sí mismo puede ser programado para escribir código para usted. Creo que ilustra fuertemente la diferencia entre legibilidad y mantenibilidad . El código es difícil de leer, pero si la enumeración tiene unos cientos de elementos, puede agregar, eliminar o reorganizar elementos y aún así asegurarse de que el código generado no tenga errores.
fuente
#define TEST_1 hello #define TEST_2 world
entoncestypedef enum { TEST_1, TEST_2 } test_t;
y luego crear una tabla de búsqueda de cadenas que use una macro stringify:const char* table[]= { STRINGIFY(TEST_1), STRINGIFY(TEST_2), };
ya hay múltiples respuestas que sugieren soluciones similares. Mucho más legible.Aquí está el método Old Skool (solía usarse ampliamente en gcc) usando solo el preprocesador C. Es útil si está generando estructuras de datos discretas pero necesita mantener el orden consistente entre ellas. Las entradas en mylist.tbl pueden, por supuesto, extenderse a algo mucho más complejo.
test.cpp:
Y luego mylist.tbl:
fuente
En c ++ así:
fuente
de http://www.codeproject.com/Articles/42035/Enum-to-String-and-Vice-Versa-in-C y después
insertar
Funciona bien si los valores en la enumeración no están duplicados.
Código de muestra para convertir un valor de enumeración a cadena:
Código de muestra para todo lo contrario:
fuente
Gracias James por tu sugerencia. Fue muy útil, así que implementé al revés para contribuir de alguna manera.
fuente
Para extender la respuesta de James, alguien quiere un código de ejemplo para admitir enum define con valor int, también tengo este requisito, así que aquí está mi manera:
El primero es la macro de uso interno, que FOR_EACH utiliza:
Y, aquí está la macro de definición:
Entonces, al usarlo, puede escribir así:
que se expandirá a:
La idea básica es definir un SEQ, que cada elemento es una TUPLE, para que podamos poner un valor de suma para el miembro enum. En FOR_EACH loop, verifique el tamaño del elemento TUPLE, si el tamaño es 2, expanda el código a KEY = VALUE, de lo contrario, simplemente mantenga el primer elemento de TUPLE.
Debido a que la SEQ de entrada es en realidad TUPLEs, por lo que si desea definir las funciones STRINGIZE, es posible que primero deba procesar los enumeradores de entrada, aquí está la macro para hacer el trabajo:
La macro
DEFINE_ENUM_WITH_STRING_CONVERSIONS_FIRST_ELEM_SEQ
solo mantendrá el primer elemento en cada TUPLA, y luego se convertirá a SEQ, ahora modifica el código de James, tendrá todo el poder.Mi implementación tal vez no sea la más simple, por lo que si no encuentra ningún código limpio, mía para su referencia.
fuente
Solución limpia y segura en puro estándar C:
Salida
Razón fundamental
Al resolver el problema central "tener constantes de enumeración con las cadenas correspondientes", un programador sensible presentará los siguientes requisitos:
El primer requisito, y quizás también el segundo, se puede cumplir con varias soluciones macro desordenadas, como el infame truco "x macro" u otras formas de macro magia. El problema con tales soluciones es que te dejan con un lío de macros misteriosas completamente ilegible: no cumplen con el tercer requisito anterior.
Lo único que se necesita aquí es tener una tabla de búsqueda de cadenas, a la que podamos acceder utilizando la variable enum como índice. Tal tabla debe corresponder naturalmente directamente a la enumeración y viceversa. Cuando uno de ellos se actualiza, el otro también debe actualizarse, o no funcionará.
Explicación del código.
Supongamos que tenemos una enumeración como
Esto se puede cambiar a
Con la ventaja de que estas constantes macro ahora se pueden usar en otro lugar, por ejemplo, para generar una tabla de búsqueda de cadenas. La conversión de una constante de preprocesador a una cadena se puede hacer con una macro "stringify":
Y eso es. Al usar
hello
, obtenemos la constante enum con el valor 0. Al usartest_str[hello]
obtenemos la cadena "hola".Para que la enumeración y la tabla de búsqueda correspondan directamente, debemos asegurarnos de que contengan la misma cantidad de elementos. Si alguien mantiene el código y solo cambia la enumeración, y no la tabla de búsqueda, o viceversa, este método no funcionará.
La solución es tener la enumeración para decirle cuántos elementos contiene. Hay un truco C de uso común para esto, simplemente agregue un elemento al final, que solo cumple el propósito de decir cuántos elementos tiene la enumeración:
Ahora podemos comprobar en tiempo de compilación que el número de elementos en la enumeración es tanto como el número de elementos en la tabla de búsqueda, preferiblemente con una afirmación estática C11:
(También hay formas feas pero totalmente funcionales de crear afirmaciones estáticas en versiones anteriores del estándar C, si alguien insiste en usar compiladores de dinosaurios. En cuanto a C ++, también admite afirmaciones estáticas).
Como nota al margen, en C11 también podemos lograr una mayor seguridad de tipo cambiando la macro stringify:
(
int
porque las constantes de enumeración son en realidad de tipoint
, notest_t
)Esto evitará que se compile código como
STRINGIFY(random_stuff)
.fuente
#define
, agregue una referencia a la que se define en la declaración de enumeración y la tabla de búsqueda. Si te molestas al agregar esas líneas, el programa no se compilará. Los números que agregué a los identificadores de ninguna manera son obligatorios, también podría escribir#define APPLES hello
y#define ORANGES world
seguir,typedef enum { APPES, ORANGES, TEST_N } test_t;
y así sucesivamente.Lo que hice es una combinación de lo que he visto aquí y en preguntas similares en este sitio. Hice esto es Visual Studio 2013. No lo he probado con otros compiladores.
En primer lugar, defino un conjunto de macros que harán los trucos.
A continuación, defina una sola macro que creará la clase enum y las funciones para obtener las cadenas.
Ahora definir un tipo de enumeración y tener cadenas para él se vuelve realmente fácil. Todo lo que necesitas hacer es:
Las siguientes líneas se pueden usar para probarlo.
Esto generará:
Creo que es muy limpio y fácil de usar. Hay algunas limitaciones:
Si puedes evitar esto. Creo que, especialmente cómo usarlo, esto es agradable y delgado. Ventajas:
fuente
Una solución limpia a este problema sería:
Lo bueno de esta solución es que es simple y también se puede construir fácilmente la función mediante copiar y reemplazar. Tenga en cuenta que si va a hacer muchas conversiones y su enumeración tiene demasiados valores posibles, esta solución podría ser intensiva en CPU.
fuente
Llego un poco tarde, pero esta es mi solución usando g ++ y solo bibliotecas estándar. He tratado de minimizar la contaminación del espacio de nombres y eliminar cualquier necesidad de volver a escribir nombres enum.
El archivo de encabezado "my_enum.hpp" es:
Ejemplo de uso:
Esto generará:
Solo tiene que definir todo una vez, su espacio de nombres no debe contaminarse y todo el cálculo se realiza solo una vez (el resto son solo búsquedas). Sin embargo, no obtiene la seguridad de tipo de las clases enum (todavía son enteros cortos), no puede asignar valores a las enumeraciones, tiene que definir enumeraciones en algún lugar donde pueda definir espacios de nombres (por ejemplo, globalmente).
No estoy seguro de qué tan bueno es el rendimiento en esto, o si es una buena idea (aprendí C antes que C ++, así que mi cerebro todavía funciona de esa manera). Si alguien sabe por qué es una mala idea, no dude en señalarlo.
fuente
Es 2017 pero la pregunta sigue viva
Otra forma más:
Salidas:
fuente
Esta es una versión de enumeración extendida de clase elaborada ... no agrega ningún otro valor de enumeración que no sea el proporcionado.
Uso: DECLARE_ENUM_SEQ (CameraMode, (3), Fly, FirstPerson, PerspectiveCorrect)
fuente
Necesitaba que esto funcionara en ambas direcciones Y con frecuencia incrustaba mis enumeraciones dentro de una clase que contenía, así que comencé con la solución de James McNellis muy, muy por encima de estas respuestas, pero hice esta solución. Tenga en cuenta también que prefiero enum class en lugar de solo enum, lo que complica un poco la respuesta.
Para usarlo dentro de una clase, podrías hacer algo como esto:
Y escribí una prueba CppUnit, que demuestra cómo usarla:
Debe elegir qué macro usar, DEFINE_ENUMERATION o DEFINE_ENUMERATION_INSIDE_CLASS. Verá que usé este último cuando definí ComponentStatus :: Status pero usé el primero cuando solo definí Status. La diferencia es simple. Dentro de una clase, prefiero los métodos to / from como "static" y si no está en una clase, uso "inline". Diferencias triviales, pero necesarias.
Desafortunadamente, no creo que haya una manera limpia de evitar tener que hacer esto:
aunque podría crear manualmente un método en línea después de su definición de clase que simplemente se encadena al método de clase, algo como:
fuente
Mi propia respuesta, no usar impulso: usar mi propio enfoque sin una gran magia de definición, y esta solución tiene la limitación de no poder definir un valor de enumeración específico.
La última versión se puede encontrar en github aquí:
https://github.com/tapika/cppscriptcore/blob/master/SolutionProjectModel/EnumReflect.h
fuente
Hay muchas otras respuestas a esto, pero creo que una mejor manera es usar las características de C ++ 17 y usar constexpr para que las traducciones se realicen en tiempo de compilación. Esto es de tipo seguro y no necesitamos jugar con macros. Vea abajo:
Esto se puede usar fácilmente para que los errores de clave de cadena se detecten en tiempo de compilación:
El código es más detallado que algunas otras soluciones, pero podemos hacer fácilmente la conversión de Enum a String y la conversión de String a Enum en tiempo de compilación y detectar errores de tipo. Con algunas de las características futuras de C ++ 20, esto probablemente se pueda simplificar un poco más.
fuente