Hay una posibilidad de convertir enumerador de los nombres de cadena en C?

InformationsquelleAutor Misha | 2012-03-28

8 Comentarios

  1. 166

    Una manera, haciendo que el preprocesador de hacer el trabajo. También se asegura de que sus enumeraciones y las cadenas están en sincronía.

    #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 se hace, tendrás:

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

    Entonces usted podría hacer algo como:

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

    Si el caso de uso es, literalmente, acaba de imprimir el enum nombre, agregue las siguientes macros:

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

    A continuación, hacer:

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

    En este caso, puede parecer que los dos-nivel macro es superfluo, sin embargo, debido a la forma de stringification obras en C, es necesario en algunos casos. Por ejemplo, digamos 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 se stringify la entrada de foo en lugar de ampliarlo a ser apple. Mediante el uso de xstr la expansión de macro se realiza en primer lugar, luego que el resultado es un string.

    Ver Stringification para obtener más información.

    • Esto es perfecto, pero soy incapaz de entender lo que realmente está sucediendo. :O
    • También ¿cómo hace uno para convertir una cadena a una enumeración en el caso anterior?
    • Hay un par de maneras en que se podía hacer, dependiendo de lo que usted está tratando de lograr?
    • Muy simple como usted la describe. Sólo quiero enumeración de cadena y la cadena de enum para un tipo de enumeración en un archivo de encabezado. No quiero crear un archivo de implementación (para C++/Objective-C), cuando se trata con las enumeraciones.
    • Usted puede recorrer en FRUIT_STRING matriz y hacer una comparación de cadenas. Si se encuentra una coincidencia, entonces el índice es el valor de la ENUMERACIÓN, suponiendo que no dispersa de las enumeraciones.
    • Sólo quiero darle las gracias por una hermosa solución
    • Si usted no desea contaminar el espacio de nombres con manzana y naranja… puede prefijo con #define GENERATE_ENUM(ENUM) PREFIX##ENUM,
    • este enfoque tiene la desventaja de que no es fácil encontrar donde una enumeración artículo fue definido.
    • La última línea que contiene str(apple) no funcionará si apple es un valor int en lugar de que el texto literal de «manzana». En cambio, si una variable llamada i fueron aprobadas sería de impresión «enumeración de apple como una cadena: i». Lo cual tiene sentido porque esto funciona en el preprocesador nivel.
    • Gracias por este excelente respuesta. En lugar de utilizar esta solución exacta (por un problema un poco diferente), me dio la idea de utilizar macros con algunos parámetros adicionales para generar instrucciones IF [si (valor.es igual a(F(CADENA))) entonces index = CÓDIGO;] – esto me salvó de toneladas de memoria!
    • Para aquellos que vienen a través de este post, este método de utilizar una macro de la lista para enumerar varios elementos en un programa denominado «X macros».
    • Puede que esto de alguna manera ser utilizado con diferentes valores para las enumeraciones otros que 0,1,2,3 … ?

  2. 23

    En una situación donde usted tiene este:

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

    Me gusta poner esto en el archivo de encabezado donde la enumeración se define:

    static inline char *stringFromFruit(enum fruit f)
    {
        static const char *strings[] = { "apple", "orange", "grape", "banana", /* continue for rest of values */ };
    
        return strings[f];
    }
    • Para la vida de mí no puedo ver cómo esto ayuda. Podrías ampliar un poco para hacerla más evidente.
    • OK, ¿cómo ayudar? Está diciendo que es más fácil escribir enumToString(apple) que tipo de "apple"? No es que no hay ningún tipo de seguridad en cualquier lugar. A menos que me falta algo lo que se sugiere aquí es inútil y sólo tiene éxito en la ofuscación de código.
    • y ahí es donde la función es. Usted será devuelto una cadena no válida si se invoca stringFromFruit() con un valor no válido, el macro es simplemente ahí, porque eso es lo que me gusta usar. Nada más. Puedo entender que el OP no se considere que la práctica, eso es lo que la segunda respuesta es que.
    • OK, voy a ver ahora. La macro es falso, en mi opinión, y yo sugiero que usted lo elimine.
    • Sí, eso es fácil. Pero tengo ya una larga enumeración de estructura y necesito imprimir sus nombres, en lugar de un int.
    • ¿por qué editar mi código? Esto no es C#, las enumeraciones se prevé que los nombres en minúsculas…
    • lo siento por eso, yo en realidad estaba buscando la solución en el Objetivo C. así que he editado en Objective C, sólo le dio un CamelCase. lo siento de nuevo. 🙂
    • comentarios a hablar de macro. Donde es?
    • se puede ver en la la revisión de la historia, pero no es super útil para la mayoría de los casos, por lo que se retira.
    • Muchas veces he necesitado un int-a-conversión de cadena cuando acabo el int y la necesidad de imprimir la información de depuración. Aunque puede ser más fácil para el tipo de «apple» que enum.tostring(apple), que no es el caso de uso. En mi caso tengo un dispositivo incorporado que devuelve enteros, y el lado del host necesita para imprimir el legibles valor.
    • Esto también es un inconveniente para mantener. Si puedo insertar una nueva enumeración tengo que rememeber duplicar que también en la matriz, en la posición correcta.
    • A la vuelta se deseche el calificador const introducido por la definición de la matriz. Por lo tanto se debe eliminar o hay que agregar a la firma de función, por ejemplo: static inline const char *stringFromFruit(enum fruit f).

  3. 15

    No hay una manera sencilla para logra esto directamente. Pero P99 ha macros que permiten crear este tipo de función de forma automática:

     P99_DECLARE_ENUM(color, red, green, blue);

    en un archivo de encabezado, y

     P99_DEFINE_ENUM(color);

    en una unidad de compilación (.archivo c) que debería hacer el truco, en el ejemplo a continuación, la función sería llamado color_getname.

    • Cómo puedo tirar de este lib en?
  4. 10

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

    Secuencial de las enumeraciones

    Tras la invención de Stefan Ram, secuencial de las enumeraciones (sin que explícitamente indica el índice, por ejemplo,enum {foo=-1, foo1 = 1}) puede ser realizado como este genio truco:

    #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 nos 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.

    No-Secuencial de las enumeraciones

    Desde quería mapa de los códigos de error de las definiciones que son la matriz de cadena, por lo que puedo anexar la cruda definición de error con el código de error (por ejemplo,"The error is 3 (LC_FT_DEVICE_NOT_OPENED)."), me extendió el código en que la forma en que usted puede determinar fácilmente el índice necesario para los respectivos valores de enumeración:

    #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 de 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 lugar a las siguientes capacidades de implementación:

    LC_errores__strings[-1]
    ==> LC_errores__strings[LC_RESPONSE_GENERIC_ERROR]
    ==> «LC_RESPONSE_GENERIC_ERROR»

  5. 2

    Una función como la que sin validar el enum es un poco peligroso. Yo sugiero utilizar una instrucción switch. Otra ventaja es que puede ser utilizado para las enumeraciones que tienen valores definidos, por ejemplo, para las banderas, donde los valores son 1,2,4,8,16 etc.

    También hay que poner todo su enumeración cadenas de una matriz:-

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

    definir los índices en un archivo de encabezado:-

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

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

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

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

    Hacer una función con una instrucción switch, este debe devolver un const char * porque las cadenas estáticas consts:-

    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 la programación con Windows, a continuación, el ID_ valores pueden ser los valores de los recursos.

    (Si el uso de C++, a continuación, todas las funciones pueden tener el mismo nombre.

    string EnumToString(fruit e);

    )

  6. 1

    Una alternativa más simple a Hokyo del «No-Secuencial de las enumeraciones» la respuesta, basada en el uso de designadores de crear una instancia de la matriz de cadena:

    #define NAMES C(RED, 10)C(GREEN, 20)C(BLUE, 30)
    #define C(k, v) k = v,
    enum color { NAMES };
    #undef C
    #define C(k, v) [v] = #k,    
    const char * const color_name[] = { NAMES };
  7. 1

    Usted no tiene que confiar en que el preprocesador para garantizar su enumeraciones y las cadenas están en sincronía. Para mí el uso de macros tienden a hacer que el código sea más difícil de leer.

    Mediante La Enumeración Y Una Matriz De Cadenas

    enum fruit                                                                   
    {
    APPLE = 0, 
    ORANGE, 
    GRAPE,
    BANANA,
    /* etc. */
    FRUIT_MAX                                                                                                                
    };   
    const char * const fruit_str[] =
    {
    [BANANA] = "banana",
    [ORANGE] = "orange",
    [GRAPE]  = "grape",
    [APPLE]  = "apple",
    /* etc. */  
    };

    Nota: las cadenas en el fruit_str matriz no tiene que ser declarado en el mismo orden de la enumeración de elementos.

    Cómo Usarlo

    printf("enum apple as a string: %s\n", fruit_str[APPLE]);

    La Adición De Un Tiempo De Compilación, Verificación

    Si usted tiene miedo de olvidar a una cuerda, puede agregar la siguiente verificación:

    #define ASSERT_ENUM_TO_STR(sarray, max) \                                       
    typedef char assert_sizeof_##max[(sizeof(sarray)/sizeof(sarray[0]) == (max)) ? 1 : -1]
    ASSERT_ENUM_TO_STR(fruit_str, FRUIT_MAX);

    Un error sería informado en tiempo de compilación si la cantidad de enumeración de elementos no coincide con el importe de las cadenas en la matriz.

  8. -2

    Me suelen hacer esto:

    #define COLOR_STR(color)                            \
    (RED       == color ? "red"    :                \
    (BLUE     == color ? "blue"   :                \
    (GREEN   == color ? "green"  :                \
    (YELLOW == color ? "yellow" : "unknown"))))   
    • Esto es simplemente ridicolous
    • Esto no es una mala respuesta. Es claro, sencillo y fácil de entender. Si usted está trabajando en sistemas en los que otras personas necesitan para leer y entender el código rápidamente, la claridad es muy importante. Yo no recomendaría el uso del preprocesador trucos a menos que estén completamente comentado o descrita en un estándar de codificación.

Dejar respuesta

Please enter your comment!
Please enter your name here