Como converter nomes enum para string em c

Existe a possibilidade de converter nomes de enumeradores para string em C?

Uma maneira é fazer com que o pré-processador faça o trabalho. Isso também garante que suas enums e strings estejam em sincronia.

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

Depois que o pré-processador terminar, você terá:

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

Então você poderia fazer algo como:

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

Se o caso de uso estiver literalmente apenas imprimindo o nome do enum, adicione as seguintes macros:

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

Então faça:

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

Neste caso, pode parecer que a macro de dois níveis é supérflua, no entanto, devido ao funcionamento da string em C, isso é necessário em alguns casos. Por exemplo, digamos que queremos usar um #define com um enum:

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

A saída seria:

 foo apple 

Isso ocorre porque str irá restringir a input foo em vez de expandi-la para ser apple. Ao usar xstr, a expansão de macro é feita primeiro e, em seguida, esse resultado é estringificado.

Veja Stringification para mais informações.

Em uma situação em que você tem isso:

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

Eu gosto de colocar isso no arquivo de header onde o enum é definido:

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

Não há uma maneira simples de alcançar isso diretamente. Mas o P99 tem macros que permitem criar esse tipo de function automaticamente:

  P99_DECLARE_ENUM(color, red, green, blue); 

em um arquivo de header e

  P99_DEFINE_ENUM(color); 

em uma unidade de compilation (arquivo .c) deve fazer o truque, nesse exemplo a function então seria chamada color_getname .

Eu encontrei um truque de pré-processamento de C que está fazendo o mesmo trabalho sem declarar uma cadeia de matriz dedicada (Fonte: http://userpage.fu-berlin.de/~ram/pub/pub_jf47ht81Ht/c_preprocessor_applications_en ).

Enums seqüenciais

Seguindo a invenção de Stefan Ram, enums seqüenciais (sem explicitamente declarar o índice, por exemplo, enum {foo=-1, foo1 = 1} ) podem ser realizados como este truque de gênio:

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

Isso dá o seguinte resultado:

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

A cor é vermelha.
Existem 3 colors.

Enums não sequenciais

Desde que eu queria mapear as definições dos códigos de erro para string array, então eu posso append a definição de erro bruta ao código de erro (ex: "The error is 3 (LC_FT_DEVICE_NOT_OPENED)." ), Eu estendi o código dessa forma que você pode facilmente determinar o índice necessário para os 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]; 

Neste exemplo, o pré-processador C irá gerar o seguinte 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", }; 

Isso resulta nos seguintes resources de implementação:

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

Uma function como essa sem validar o enum é um pouco perigosa. Eu sugiro usar uma instrução switch. Outra vantagem é que isso pode ser usado para enums que tenham valores definidos, por exemplo, para flags onde os valores são 1,2,4,8,16 etc.

Além disso, coloque todas as suas cadeias de enumeração juntas em uma matriz:

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

defina os índices em um arquivo de header: –

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

Isso facilita a produção de versões diferentes, por exemplo, se você quiser fazer versões internacionais do programa com outros idiomas.

Usando uma macro, também no arquivo de header: –

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

Faça uma function com uma instrução switch, isso deve retornar um const char * porque as strings static 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]; } 

Se estiver programando com o Windows, os valores de ID_ podem ser valores de resources.

(Se estiver usando C ++, todas as funções podem ter o mesmo nome.

 string EnumToString(fruit e); 

)

Uma alternativa mais simples à resposta “Non-Sequential enums” do Hokyo, baseada no uso de designadores para instanciar o array de strings:

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

Eu costumo fazer isso:

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