如何将枚举名称转换为c中的string
有可能将枚举数的名称转换为C中的string?
一种方法,使预处理器做的工作。 它也确保你的枚举和string是同步的。
#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) };
预处理器完成后,您将拥有:
enum FRUIT_ENUM { apple, orange, grape, banana, }; static const char *FRUIT_STRING[] = { "apple", "orange", "grape", "banana", };
那么你可以做一些事情:
printf("enum apple as a string: %s\n",FRUIT_STRING[apple]);
如果用例实际上只是打印枚举名称,请添加以下macros:
#define str(x) #x #define xstr(x) str(x)
然后做:
printf("enum apple as a string: %s\n", xstr(apple));
在这种情况下,可能看起来像两级macros是多余的,但是由于在C中如何工作,在某些情况下是必要的。 例如,假设我们想要使用带enum的#define:
#define foo apple int main() { printf("%s\n", str(foo)); printf("%s\n", xstr(foo)); }
输出将是:
foo apple
这是因为str会将inputfoo串联起来而不是将其扩展为apple。 通过使用xstr,首先完成macros扩展,然后将结果string化。
有关更多信息,请参阅string化
在有这样的情况下:
enum fruit { apple, orange, grape, banana, // etc. };
我喜欢把它放在枚举定义的头文件中:
static inline char *stringFromFruit(enum fruit f) { static const char *strings[] = { "apple", "orange", "grape", "banana", /* continue for rest of values */ }; return strings[f]; }
没有简单的方法直接实现这一点。 但P99有macros,可以让你自动创build这样的function:
P99_DECLARE_ENUM(color, red, green, blue);
在头文件中,和
P99_DEFINE_ENUM(color);
在一个编译单元(.c文件)中应该做的伎俩,在那个例子中,该函数将被称为color_getname
。
我发现了一个C预处理器的技巧, 没有声明专用的数组string(Source: http : //userpage.fu-berlin.de/~ram/pub/pub_jf47ht81Ht/c_preprocessor_applications_en )就可以做同样的工作。
顺序枚举
在Stefan Ram的发明之后,顺序枚举(没有明确指出索引,例如enum {foo=-1, foo1 = 1}
)可以像这个天才技巧一样实现:
#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 };
这给出了以下结果:
int main( void ) { printf( "The color is %s.\n", color_name[ RED ]); printf( "There are %d colors.\n", TOP ); }
颜色是红色。
有3种颜色。
非顺序枚举
因为我想将错误代码定义映射到数组string,所以我可以将原始错误定义追加到错误代码中(例如"The error is 3 (LC_FT_DEVICE_NOT_OPENED)."
),所以我扩展了代码轻松确定各个枚举值所需的索引:
#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];
在这个例子中, C预处理器将生成以下代码 :
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", };
这导致了以下实现function:
LC_errors__strings [-1] ==> LC_errors__strings [LC_RESPONSE_GENERIC_ERROR] ==>“LC_RESPONSE_GENERIC_ERROR”
一个没有validation枚举的函数是一件小事。 我build议使用switch语句。 另一个优点是,这可以用于已定义值的枚举,例如值为1,2,4,8,16等的标志
也把所有的枚举string放在一个数组中: –
static const char * allEnums[] = { "Undefined", "apple", "orange" /* etc */ };
在头文件中定义索引:
#define ID_undefined 0 #define ID_fruit_apple 1 #define ID_fruit_orange 2 /* etc */
这样做可以更容易地生成不同的版本,例如,如果您想用其他语言制作程序的国际版本。
使用一个macros,也在头文件中: –
#define CASE(type,val) case val: index = ID_##type##_##val; break;
用switch语句做一个函数,这应该返回一个const char *
因为stringstatic 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]; }
如果用Windows编程,则ID_值可以是资源值。
(如果使用C ++,那么所有的函数都可以有相同的名字。
string EnumToString(fruit e);
)
我通常这样做:
#define COLOR_STR(color) \ (RED == color ? "red" : \ (BLUE == color ? "blue" : \ (GREEN == color ? "green" : \ (YELLOW == color ? "yellow" : "unknown"))))