Existe-t-il une possibilité de convertir les noms des énumérateurs en chaîne de caractères en C ?
C'est parfait, mais je n'arrive pas à comprendre ce qui se passe vraiment. :O
Existe-t-il une possibilité de convertir les noms des énumérateurs en chaîne de caractères en C ?
Une façon de faire, faire faire le travail par le préprocesseur. Cela permet également de s'assurer que vos enums et vos chaînes de caractères sont synchronisés.
#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",
};
Alors vous pourriez faire quelque chose comme :
printf("enum apple as a string: %s\n",FRUIT_STRING[apple]);
Si le cas d'utilisation est littéralement juste l'impression du nom de l'enum, ajoutez les macros suivantes :
#define str(x) #x
#define xstr(x) str(x)
Alors, faites-le :
printf("enum apple as a string: %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 stringification fonctionne en C, elle 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
Ceci est dû au fait que str transformera l'entrée foo en chaîne plutôt que de l'étendre en pomme. En utilisant xstr, l'expansion de la macro est d'abord effectuée, puis le résultat est transformé en chaîne.
Voir Stringification pour plus d'informations.
Dans une situation où vous avez ceci :
enum fruit {
apple,
orange,
grape,
banana,
// etc.
};
J'aime mettre cela dans le fichier d'en-tête où l'enum est défini :
static inline char *stringFromFruit(enum fruit f)
{
static const char *strings[] = { "apple", "orange", "grape", "banana", /* continue for rest of values */ };
return strings[f];
}
Je ne vois pas en quoi cela peut aider. Pourriez-vous développer un peu pour rendre cela plus évident.
OK, en quoi cela aide-t-il ? Tu veux dire que c'est plus facile de taper enumToString(apple)
que de taper "apple"
? Ce n'est pas comme s'il y avait une sécurité de type quelque part. À moins que je ne manque quelque chose, ce que vous suggérez ici est inutile et ne sert qu'à obscurcir le code.
@DavidHeffernan et c'est là que la fonction entre en jeu. Vous obtiendrez une chaîne de caractères invalide si vous invoquez la fonction stringFromFruit()
avec une valeur invalide, la macro est simplement là parce que c'est ce que j'aime utiliser. Rien de plus. Je peux comprendre que l'OP ne considère pas cela comme pratique, c'est pourquoi la deuxième réponse est faite.
J'ai trouvé une astuce du préprocesseur C qui fait le même travail sans déclaration d'une chaîne de tableau dédiée (Source : http://userpage.fu-berlin.de/~ram/pub/pub_jf47ht81Ht/c_preprocessor_applications_fr ).
Suite à l'invention de Stefan Ram, les enums séquentiels (sans indication explicite de l'indice, par ex. enum {foo=-1, foo1 = 1}
) peut être réalisé grâce à cette astuce géniale :
#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 };
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 existe 3 couleurs.
Puisque je voulais faire correspondre les définitions des codes d'erreur à une chaîne de tableau, je peux ajouter la définition brute de l'erreur au code d'erreur (par ex. "The error is 3 (LC_FT_DEVICE_NOT_OPENED)."
), j'ai étendu le code de manière à ce que vous puissiez facilement déterminer l'index requis pour les valeurs respectives de l'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];
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__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", };
Il en résulte les capacités de mise en œuvre suivantes :
LC_errors__strings[-1] ==> LC_errors__strings[LC_RESPONSE_GENERIC_ERROR] ==> "LC_RESPONSE_GENERIC_ERROR" (EN ANGLAIS)
Il n'existe pas de moyen simple d'y parvenir directement. Mais P99 dispose de 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 serait alors appelée color_getname
.
Vous n'avez pas besoin de compter sur le préprocesseur pour vous assurer que vos enums et vos chaînes de caractères sont synchronisés. Pour moi, l'utilisation de macros a tendance à rendre le code plus difficile à lire.
enum fruit
{
APPLE = 0,
ORANGE,
GRAPE,
BANANA,
/* etc. */
FRUIT_MAX
};
const char * const fruit_str[] =
{
[BANANA] = "banana",
[ORANGE] = "orange",
[GRAPE] = "grape",
[APPLE] = "apple",
/* etc. */
};
_Remarque : les chaînes de caractères dans les fruit_str
n'ont pas besoin d'être déclarés dans le même ordre que les éléments de l'énumération._
printf("enum apple as a string: %s\n", fruit_str[APPLE]);
Si vous avez peur d'oublier une chaîne, vous pouvez ajouter le contrôle suivant :
#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);
Une erreur serait signalée au moment de la compilation si le nombre d'éléments de l'énumération ne correspond pas au nombre de chaînes dans le tableau.
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.