Comment convertir les noms enum en chaîne dans c

Est-il possible de convertir les noms d’énumérateur en chaîne dans C?

    Un moyen, faisant que le préprocesseur fasse le travail. Cela garantit également que vos énumérations et vos chaînes sont synchronisées.

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

    Une fois le préprocesseur terminé, vous aurez:

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

    Ensuite, vous pourriez faire quelque chose comme:

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

    Si le cas d’utilisation est littéralement en train d’imprimer le nom enum, ajoutez les macros suivantes:

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

    Alors fais:

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

    Dans ce cas, il peut sembler que la macro à deux niveaux est superflue. Cependant, en raison de la façon dont la cordification fonctionne en C, cela est nécessaire dans certains cas. Par exemple, disons que nous voulons utiliser un #define avec un enum:

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

    La sortie serait:

     foo apple 

    C’est parce que str va figer le foo d’entrée plutôt que de l’étendre à apple. En utilisant xstr, l’expansion de la macro est effectuée en premier, puis ce résultat est stratifié.

    Voir Ssortingngification pour plus d’informations.

    Dans une situation où vous avez ceci:

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

    J’aime mettre ceci dans le fichier d’en-tête où l’enum est défini:

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

    Il n’y a pas de moyen simple pour y parvenir directement. Mais P99 a des macros qui vous permettent de créer automatiquement ce type de fonction:

      P99_DECLARE_ENUM(color, red, green, blue); 

    dans un fichier d’en-tête et

      P99_DEFINE_ENUM(color); 

    dans une unité de compilation (fichier .c) devrait alors faire l’affaire, dans cet exemple la fonction s’appellerait alors color_getname .

    J’ai trouvé un truc de préprocesseur C qui fait le même travail sans déclarer une chaîne de tableau dédiée (Source: http://userpage.fu-berlin.de/~ram/pub/pub_jf47ht81Ht/c_preprocessor_applications_en ).

    Énumérations séquentielles

    Après l’invention de Stefan Ram, les enum {foo=-1, foo1 = 1} séquentielles (sans énoncer explicitement l’index, par exemple enum {foo=-1, foo1 = 1} ) peuvent être réalisées comme cette astuce de génie:

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

    Cela donne le résultat suivant:

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

    La couleur est ROUGE.
    Il y a 3 couleurs.

    Énumérations non séquentielles

    Comme je voulais mapper les définitions de codes d’erreur à la chaîne de tableau, de sorte que je puisse append la définition d’erreur brute au code d’erreur (par exemple, "The error is 3 (LC_FT_DEVICE_NOT_OPENED)." ) "The error is 3 (LC_FT_DEVICE_NOT_OPENED)." déterminer facilement l’index requirejs pour les valeurs enum respectives:

     #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__ssortingngs[] = { LC_ERRORS_NAMES }; static const char** LC_errors__ssortingngs = &__LC_errors__ssortingngs[10]; 

    Dans cet exemple, le préprocesseur C générera le code suivant :

     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__ssortingngs[] = { "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", }; 

    Cela se traduit par les capacités d’implémentation suivantes:

    LC_errors__ssortingngs [-1] ==> LC_errors__ssortingngs [LC_RESPONSE_GENERIC_ERROR] ==> “LC_RESPONSE_GENERIC_ERROR”

    Une fonction comme celle-là sans validation de la énumération est un peu dangereuse. Je suggère d’utiliser une instruction switch. Un autre avantage est que cela peut être utilisé pour les énumérations qui ont des valeurs définies, par exemple pour les indicateurs où les valeurs sont 1,2,4,8,16 etc.

    Mettez également toutes vos chaînes enum dans un tableau: –

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

    définir les index dans un fichier d’en-tête: –

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

    Cela facilite la production de différentes versions, par exemple si vous souhaitez créer des versions internationales de votre programme avec d’autres langues.

    En utilisant une macro, également dans le fichier d’en-tête: –

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

    Faire une fonction avec une instruction switch, cela devrait retourner un const char * car les constantes statiques des chaînes:

     const char * FruitSsortingng(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 vous programmez avec Windows, les valeurs ID_ peuvent être des valeurs de ressource.

    (Si vous utilisez C ++, toutes les fonctions peuvent avoir le même nom.

     ssortingng EnumToSsortingng(fruit e); 

    )

    Une alternative plus simple à la réponse “Enums non séquentiels” d’Hokyo, basée sur l’utilisation de désignateurs pour instancier le tableau de chaînes:

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

    Je le fais habituellement:

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