printf("enum apple as a string: %s\n",FRUIT_STRING[apple]);
如果用例实际上只是打印枚举名,那么添加以下宏:
#define str(x) #x
#define xstr(x) str(x)
那就这样做:
printf("enum apple as a string: %s\n", xstr(apple));
在这种情况下,两级宏似乎是多余的,但是,由于字符串化在 C 中的工作方式,在某些情况下它是必要的。例如,假设我们想在枚举中使用 # Definition:
#define foo apple
int main() {
printf("%s\n", str(foo));
printf("%s\n", xstr(foo));
}
产出将是:
foo
apple
This is because str will stringify the input foo rather than expand it to be apple. By using xstr the macro expansion is done first, then that result is stringified.
随着 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 );
}
颜色是红色。
有三种颜色。
Non-Sequential enums
因为我想将错误代码定义映射为数组字符串,这样我就可以将原始错误定义附加到错误代码(例如 "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];
在这个例子中,the C preprocessor will generate the following code:
I settled on making a function whose body is updated by copying the enum over and using a regex in Vim. I use a switch-case because my enum isn't compact so we have maximum flexibility. I keep the regex as a comment in the code so it's just a matter of copy-pasting it.
const char *get_op_name(enum opcode op)
{
// To update copy the enum and apply this regex:
// s/\t\([^, ]*\).*$/\t\tcase \1: \treturn "\1";
switch (op)
{
case op_1word_ops: return "op_1word_ops";
case op_end: return "op_end";
case op_2word_ops: return "op_2word_ops";
case op_ret_v: return "op_ret_v";
case op_jmp: return "op_jmp";
case op_3word_ops: return "op_3word_ops";
case op_load_v: return "op_load_v";
case op_load_i: return "op_load_i";
case op_5word_ops: return "op_5word_ops";
case op_func2_vvv: return "op_func2_vvv";
}
return "Unknown op";
}
正则表达式跳过第一个 \t字符,然后将后面的每个既不是 ,也不是 的字符放入 \1,并匹配行的其余部分以删除所有内容。然后使用 \1作为枚举标签,它以 case <label>: return "<label>";格式重新制作行。注意,在这篇文章中它看起来很差,只是因为 StackOverflow 使用4空间表格,而在 Vim 中我使用8空间表格,所以您可能需要编辑样式的正则表达式。