在一些遗留代码中,我有很多枚举,以及一个巨大的切换案例。我想测试一下开关是否具有纯枚举类型。无意义的例子:
typedef enum EN
{
EN_0,
EN_1
} EN_T;
typedef enum DK
{
DK_0,
DK_1
} DK_T;
EN_T bar = ...
switch( bar )
{
case EN_0:
...
break;
case DK_1: //<-- mixed type
...
break;
}
我尝试用这个编译 gcc with -Wall -Wextra -pedantic
,并没有得到警告。有关如何测试的任何想法?作为编译器警告或专用测试代码。由于交换机和枚举都有100多个成员,因此它必须是某种程度的通用。
编辑:请注意我不关心这是否合法c,根据C标准。
这是不好的做法,编译器可以警告不正确的做法或不违反标准的潜在错误,例如 if( a = 1)...
永远是真的,完全合法,但可能是一个错误。
如果枚举上的开关不包含该枚举a.s.o的所有值,我可以使编译器发出警告。
如果编译器可以工作是首选,但如果像lint或类似的工具可以做到这一点,我也会很高兴。
不,你不能限制 switch
case
标签到特定的显式值 enum
。 (您可以在C ++中对C ++ 11感兴趣)。
如果你能改变 enum
值因此它们不相交,这可能对您有所帮助,但仅限于运行时。
从 标准 到目前为止只有一个约束标记语句
每个案例标签的表达 应该是一个整数常量
表达式并没有两个案例常量表达式相同
转换后,switch语句应具有相同的值。
只要它是一个整数常量表达式,它们是否属于不同的枚举并不重要。是的 你不能 做你想做的事 C
。
来自 文件 上 -Wswitch-enum
(假设您使用的是GCC):
“使用此选项时,枚举范围之外的案例标签也会引发警告。” AFAICK,此开关未启用 -Wall
要么 -Wextra
。
case xxx
是一个简单的关键字,具有不那么硬的典型语法。在捏时,应该可以抓住 最 通过正则表达式出现它。表达的第一个候选人就像是
(^|\s)case\s+[^:]+:
\---/anything terminated by colon
\----/drop things like 'uppercase'
这将通过文件捕获大多数(如果不是全部)典型的case关键字。然后,检测切换关键字:
)\s*{\s*case\s
应该这样做。虽然它不会寻找 switch
关键字,它检测第一个关闭括号 case
。恕我直言,足够接近,应该在大多数情况下工作。
能够检测到 case
和 switch
和它们的位置,您可以通过前面的开关对案例进行分组,并执行案例值的验证。
那当然意味着你必须编写一个可以做到这一点的小工具,但对我来说这听起来像是50到100行的非最小化代码。
当然那样 不会处理 像:
// macros:
#define SAFE_SWITCH(x) switch(assert_non_negative(x)){
#define SWITCH_END }
SAFE_SWITCH(..) case BAR: .... SWITCH_END
// clever hacks from bored programmers:
switch(parser.nodetype)
{
default: throw ..;
#include "opcodes.inc"
#include "operands.inc"
#include "keywords.inc"
}
所以这不是一个完美的解决方案,但如果您的开关/外壳是“干净的”(没有这样的宏,等等),那么值得考虑。
好吧,我会自己回答。
经过一些研究后我得出结论,至少gcc不会抱怨这个,我需要使用像pc-lint这样的额外程序。
我做了一点重写,强调了这个问题。
#include <stdio.h>
typedef enum EN
{
ZERO,
ONE
} EN_T;
typedef enum DK
{
EN, /* Danish word for One */
TO /* Danish word for Two */
} DK_T;
char* E2str( EN_T en )
{
char* ret;
switch( en )
{
case ZERO:
ret = "0";
break;
case TO:
ret = "2";
break;
}
return ret;
}
int main( void )
{
printf( "0 = %s\n", E2str( ZERO ) );
printf( "1 = %s\n", E2str( ONE ) );
return 0;
}
这将编译正常,即使有以下情况也没有警告:
gcc -o t.exe t.c -Wall -Wextra -pedantic
输出将是:
0 = 0
1 = 2
很明显,这个输出可能不是作者的意图。是的,在这个小例子中,只看代码就清楚明了。但是想象一下,这是一个200多个案例的交换机,并且交换机包含其他交换机,并且枚举的命名不像我在原始问题中的示例那样清晰。几乎不可能发现像本例中的错误。
另请注意使用 -Wextra
我启用了一个gcc检查,如果我在枚举上有一个开关就会发出警告,并且这些情况不包含该枚举中的所有值。但是因为 TO
枚举的数值为 ONE
,gcc甚至没有抱怨在交换机中丢失枚举,显然它只查看数值,而不是查看提供的枚举。
我用pc-lint测试,发现两者
---模块:t.c(C)
_
案例TO:
t.c 23警告408:类型与开关表达式不匹配
_
}
t.c 26 Info 787:enum常量'EN :: ONE'未在开关内使用
不幸的是,这不是我希望的答案,编译器完成这项工作会更好,而不是另一种工具。
仍然愿意为其他人提供更好的答案。