原题:
4、When programmers add new elements to an enumeration, they sometimes forget to add new cases to the appropriate switch statements. How could you use assertions to help detect this problem?
附录中的答案:
. . . default: ASSERT(FALSE); /*We should never get here*/ break;
把这个问题扩展下:
看到上题的时候,想到了在u-boot中加入启动命令函数的时候曾经犯过错(还有一点,boot中的启动命令函数有点分散了,应该集中下),Z先生推荐我看下《write solid code》,无意中看了android中的源码,就有了如下的记录(非常好,以前没用过):
1 //keywords.h 2 #ifndef KEYWORD 3 4 int do_chroot(int nargs, char** args); 5 int do_chdir(int nargs, char** args); 6 7 #define __MAKE_KEYWORD_ENUM__ 8 /* 9 *定义KEYWORD宏,此处用了宏的“粘贴”预处理,此时只用到了第一个参数 10 */ 11 #define KEYWORD(symbol, flags, nargs, func) K_##symbol, 12 13 enum { 14 K_NUKNOWN, 15 #endif 16 KEYWORD(capability, OPTION, 0, NULL) 17 KEYWORD(chdir, COMMAND, 1, do_chdir) 18 KEYWORD(chroot, COMMAND, 1, do_chroot) 19 20 #ifdef __MAKE_KEYWORD_ENUM__ 21 KEYWORD_COUNT, 22 }; 23 24 #undef __MAKE_KEYWORD_ENUM__ 25 #undef KEYWORD 26 #endif
1 //parser.c 2 /* 3 *第一次包含keywords.h,根据keywords.h的代码,首先得到一个枚举定义. 4 */ 5 #include "keywords.h" 6 7 /* 8 *重新定义KEYWORD宏,此处运用了宏的“#”预处理运算符 9 */ 10 #define KEYWORD(symbol, flags, nargs, func) 11 [ K_##symbol ] = { #symbol, func, nargs + 1, flags, }, 12 13 /* 14 *定义一个结构体keyword_info数组,它用来描述关键字的一些属性 15 */ 16 struct 17 { 18 const char *name; //关键字的名称 19 int (*func)(int nargs, char** args); //对应关键字的处理函数 20 unsigned char nargs; //参数个数,每个关键字的参数个数是固定的 21 unsigned char flags; //关键字的属性 22 }keyword_info[ KEYWORD_COUNT ] = { 23 [ K_UNKNOWN ] = { "unknown", 0, 0, 0,}, 24 /* 25 *第二次包含keywords.h,由于已经重新定义了KEYWORD宏, 26 *所以以前作为枚举值的关键字现在变成keyword_info数组 27 *的索引了. 28 */ 29 #include "keywords.h" 30 }; 31 32 #undef KEYWORD
在parser.c中两次包含了keywords.h文件,首次包含时列出了枚举项,可以作为后文中数组的索引;第二次包含时,由于宏已被定义,因此正好可以实现数组中的各个项。
仿照此种实现,完全可以实现习题4的解答(一个枚举项对应一个相应的处理函数即可,而且可以通过索引来完成,比switch-case快);此外可以实现代码的集中管理,否则太分散了(不然就用grep,有点麻烦)。