imprimir enum ejemplo c string enums

ejemplo - imprimir enum en c



Cómo convertir nombres enum a cadena en c (6)

En una situación donde tienes esto:

enum fruit { apple, orange, grape, banana, // etc. };

Me gusta poner esto en el archivo de encabezado donde está definida la enumeración:

static inline char *stringFromFruit(enum fruit f) { static const char *strings[] = { "apple", "orange", "grape", "banana", /* continue for rest of values */ }; return strings[f]; }

¿Existe la posibilidad de convertir nombres de enumerador a cadena en C?


Encontré un truco de preprocesador C que está haciendo el mismo trabajo sin declarar una cadena de matriz dedicada (Fuente: http://userpage.fu-berlin.de/~ram/pub/pub_jf47ht81Ht/c_preprocessor_applications_en ).

Enumeraciones secuenciales

Después de la invención de Stefan Ram, las enumeraciones secuenciales (sin indicar explícitamente el índice, por ejemplo, enum {foo=-1, foo1 = 1} ) se pueden realizar como este truco de genio:

#include <stdio.h> #define NAMES C(RED)C(GREEN)C(BLUE) #define C(x) x, enum color { NAMES TOP }; #undef C #define C(x) #x, const char * const color_name[] = { NAMES };

Esto da el siguiente resultado:

int main( void ) { printf( "The color is %s./n", color_name[ RED ]); printf( "There are %d colors./n", TOP ); }

El color es ROJO.
Hay 3 colores

Enumeraciones no secuenciales

Como quería asignar las definiciones de códigos de error a una cadena de matriz, de modo que puedo adjuntar la definición de error cruda al código de error (por ejemplo, "The error is 3 (LC_FT_DEVICE_NOT_OPENED)." ), "The error is 3 (LC_FT_DEVICE_NOT_OPENED)." el código de esa manera que puede determinar fácilmente el índice requerido para los respectivos valores enum:

#define LOOPN(n,a) LOOP##n(a) #define LOOPF , #define LOOP2(a) a LOOPF a LOOPF #define LOOP3(a) a LOOPF a LOOPF a LOOPF #define LOOP4(a) a LOOPF a LOOPF a LOOPF a LOOPF #define LOOP5(a) a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF #define LOOP6(a) a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF #define LOOP7(a) a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF #define LOOP8(a) a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF #define LOOP9(a) a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF #define LC_ERRORS_NAMES / Cn(LC_RESPONSE_PLUGIN_OK, -10) / Cw(8) / Cn(LC_RESPONSE_GENERIC_ERROR, -1) / Cn(LC_FT_OK, 0) / Ci(LC_FT_INVALID_HANDLE) / Ci(LC_FT_DEVICE_NOT_FOUND) / Ci(LC_FT_DEVICE_NOT_OPENED) / Ci(LC_FT_IO_ERROR) / Ci(LC_FT_INSUFFICIENT_RESOURCES) / Ci(LC_FT_INVALID_PARAMETER) / Ci(LC_FT_INVALID_BAUD_RATE) / Ci(LC_FT_DEVICE_NOT_OPENED_FOR_ERASE) / Ci(LC_FT_DEVICE_NOT_OPENED_FOR_WRITE) / Ci(LC_FT_FAILED_TO_WRITE_DEVICE) / Ci(LC_FT_EEPROM_READ_FAILED) / Ci(LC_FT_EEPROM_WRITE_FAILED) / Ci(LC_FT_EEPROM_ERASE_FAILED) / Ci(LC_FT_EEPROM_NOT_PRESENT) / Ci(LC_FT_EEPROM_NOT_PROGRAMMED) / Ci(LC_FT_INVALID_ARGS) / Ci(LC_FT_NOT_SUPPORTED) / Ci(LC_FT_OTHER_ERROR) / Ci(LC_FT_DEVICE_LIST_NOT_READY) #define Cn(x,y) x=y, #define Ci(x) x, #define Cw(x) enum LC_errors { LC_ERRORS_NAMES TOP }; #undef Cn #undef Ci #undef Cw #define Cn(x,y) #x, #define Ci(x) #x, #define Cw(x) LOOPN(x,"") static const char* __LC_errors__strings[] = { LC_ERRORS_NAMES }; static const char** LC_errors__strings = &__LC_errors__strings[10];

En este ejemplo, el preprocesador C generará el siguiente código :

enum LC_errors { LC_RESPONSE_PLUGIN_OK=-10, LC_RESPONSE_GENERIC_ERROR=-1, LC_FT_OK=0, LC_FT_INVALID_HANDLE, LC_FT_DEVICE_NOT_FOUND, LC_FT_DEVICE_NOT_OPENED, LC_FT_IO_ERROR, LC_FT_INSUFFICIENT_RESOURCES, LC_FT_INVALID_PARAMETER, LC_FT_INVALID_BAUD_RATE, LC_FT_DEVICE_NOT_OPENED_FOR_ERASE, LC_FT_DEVICE_NOT_OPENED_FOR_WRITE, LC_FT_FAILED_TO_WRITE_DEVICE, LC_FT_EEPROM_READ_FAILED, LC_FT_EEPROM_WRITE_FAILED, LC_FT_EEPROM_ERASE_FAILED, LC_FT_EEPROM_NOT_PRESENT, LC_FT_EEPROM_NOT_PROGRAMMED, LC_FT_INVALID_ARGS, LC_FT_NOT_SUPPORTED, LC_FT_OTHER_ERROR, LC_FT_DEVICE_LIST_NOT_READY, TOP }; static const char* __LC_errors__strings[] = { "LC_RESPONSE_PLUGIN_OK", "" , "" , "" , "" , "" , "" , "" , "" "LC_RESPONSE_GENERIC_ERROR", "LC_FT_OK", "LC_FT_INVALID_HANDLE", "LC_FT_DEVICE_NOT_FOUND", "LC_FT_DEVICE_NOT_OPENED", "LC_FT_IO_ERROR", "LC_FT_INSUFFICIENT_RESOURCES", "LC_FT_INVALID_PARAMETER", "LC_FT_INVALID_BAUD_RATE", "LC_FT_DEVICE_NOT_OPENED_FOR_ERASE", "LC_FT_DEVICE_NOT_OPENED_FOR_WRITE", "LC_FT_FAILED_TO_WRITE_DEVICE", "LC_FT_EEPROM_READ_FAILED", "LC_FT_EEPROM_WRITE_FAILED", "LC_FT_EEPROM_ERASE_FAILED", "LC_FT_EEPROM_NOT_PRESENT", "LC_FT_EEPROM_NOT_PROGRAMMED", "LC_FT_INVALID_ARGS", "LC_FT_NOT_SUPPORTED", "LC_FT_OTHER_ERROR", "LC_FT_DEVICE_LIST_NOT_READY", };

Esto da como resultado las siguientes capacidades de implementación:

LC_errors__strings [-1] ==> LC_errors__strings [LC_RESPONSE_GENERIC_ERROR] ==> "LC_RESPONSE_GENERIC_ERROR"


No hay una manera simple de lograr esto directamente. Pero P99 tiene macros que le permiten crear dicho tipo de función de forma automática:

P99_DECLARE_ENUM(color, red, green, blue);

en un archivo de cabecera, y

P99_DEFINE_ENUM(color);

en una unidad de compilación (archivo .c) debería hacer el truco, en ese ejemplo la función se llamaría color_getname .


Una forma, hacer que el preprocesador haga el trabajo. También garantiza que tus enumeraciones y cadenas estén sincronizadas.

#define FOREACH_FRUIT(FRUIT) / FRUIT(apple) / FRUIT(orange) / FRUIT(grape) / FRUIT(banana) / #define GENERATE_ENUM(ENUM) ENUM, #define GENERATE_STRING(STRING) #STRING, enum FRUIT_ENUM { FOREACH_FRUIT(GENERATE_ENUM) }; static const char *FRUIT_STRING[] = { FOREACH_FRUIT(GENERATE_STRING) };

Después de que el preprocesador haya terminado, tendrás:

enum FRUIT_ENUM { apple, orange, grape, banana, }; static const char *FRUIT_STRING[] = { "apple", "orange", "grape", "banana", };

Entonces podrías hacer algo como:

printf("enum apple as a string: %s/n",FRUIT_STRING[apple]);

Si el caso de uso literalmente solo imprime el nombre enum, agregue las siguientes macros:

#define str(x) #x #define xstr(x) str(x)

Entonces hazlo:

printf("enum apple as a string: %s/n", xstr(apple));

En este caso, puede parecer que la macro de dos niveles es superflua, sin embargo, debido a cómo funciona la codificación en C, es necesario en algunos casos. Por ejemplo, supongamos que queremos usar un #define con una enumeración:

#define foo apple int main() { printf("%s/n", str(foo)); printf("%s/n", xstr(foo)); }

La salida sería:

foo apple

Esto es porque str codificará la entrada foo en lugar de expandirla para que sea apple. Al usar xstr, la macroexpansión se hace primero, luego ese resultado se escribe en cadena.

Ver Stringification para más información.


Una función como esa sin validar la enumeración es un poco peligrosa. Sugiero usar una declaración de cambio. Otra ventaja es que esto se puede usar para enumeraciones que tienen valores definidos, por ejemplo, para indicadores donde los valores son 1,2,4,8,16, etc.

También ponga todas sus cadenas enumeradas juntas en una matriz:

static const char * allEnums[] = { "Undefined", "apple", "orange" /* etc */ };

defina los índices en un archivo de encabezado: -

#define ID_undefined 0 #define ID_fruit_apple 1 #define ID_fruit_orange 2 /* etc */

Hacer esto hace que sea más fácil producir diferentes versiones, por ejemplo, si desea hacer versiones internacionales de su programa con otros idiomas.

Usando una macro, también en el archivo de encabezado: -

#define CASE(type,val) case val: index = ID_##type##_##val; break;

Haga una función con una instrucción switch, esto debería devolver un const char * porque las cadenas consts estáticas: -

const char * FruitString(enum fruit e){ unsigned int index; switch(e){ CASE(fruit, apple) CASE(fruit, orange) CASE(fruit, banana) /* etc */ default: index = ID_undefined; } return allEnums[index]; }

Si programa con Windows, los valores ID_ pueden ser valores de recursos.

(Si usa C ++, entonces todas las funciones pueden tener el mismo nombre.

string EnumToString(fruit e);

)


Usualmente hago esto:

#define COLOR_STR(color) / (RED == color ? "red" : / (BLUE == color ? "blue" : / (GREEN == color ? "green" : / (YELLOW == color ? "yellow" : "unknown"))))