C编译器断言:如何在修复表达式的地方动态使用它们?

时间:2012-12-18 13:49:04

标签: c compiler-construction assert

我的代码广泛使用这样的编译器断言来在构建时标记错误而不是运行时,并通过在运行时不执行断言来提高性能。

#define COMPILER_ASSERT(EXPR)    switch (0) {case 0: case (EXPR):;}

一切都好。我想扩展这个以使用编译器断言来实现以下情况。假设我有一个从100个地方调用的宏,其中99个传递一个固定值,其中1个传递一个变量。我如何编写宏来使这个编译器断言在99个位置,运行时断言在最后一个。

如果我可以保证MY_FUNCTION()总是以固定值调用,我可以像这样编码。

void my_function(int x)
{
  //do something
}

#define MY_FUNCTION(X) \
  COMPILER_ASSERT(X != 0); \
  my_function(X)

//These can all take advantage of a compiler assert.
MY_FUNCTION(1);
MY_FUNCTION(SOME_HASH_DEFINE);
MY_FUNCTION(sizeof(SOME_STRUCTURE));
//This can't (this is a contrived example - actual code is complex).
int some_variable = 1;
MY_FUNCTION(some_variable);

所以,如果我不能保证X是固定的,但是想要利用每次调用MY_FUNCTION()的位置,我该怎么编码呢?类似的东西:

#define MY_FUNCTION(X) \
  if (X is a fixed value) COMPILER_ASSERT(X != 0); \
  else assert(X != 0); \ 
  my_function(X)

重新调用MY_FUNCTION()的调用只传递固定值不是我的选择。是的我可以定义MY_FUNCTION_FIXED_X和MY_FUNCTION_VARIABLE_X,但是这会将所有这些暴露给调用代码。

感谢您的帮助。 NickB

3 个答案:

答案 0 :(得分:2)

如果您的C编译器支持可变长度数组,您可以编写如下内容:

#define GENERIC_ASSERT(EXPR) \
  ((EXPR) ? (void) 0 : assert((EXPR)), (void) sizeof(char[(EXPR) ? 1 : -1]))

如果EXPR是假值编译时常量,则减少为:

(assert((EXPR)), (void) sizeof(char[-1]))

这是一个编译错误(它涉及一个负长度数组)。

如果EXPR是一个真值编译时常量,我们得到:

((void) 0), (void) 1)

如果使用真值编译时常量调用,Clang和gcc都能够将断言减少为空。

如果EXPR具有运行时值,则调用sizeof表达式会导致运行时错误(例如中止),因此assert首先通过使用逗号运算符。

不幸的是,在编译时常量情况下,gcc输出的错误消息并不是特别有启发性:

prog.c:5: error: size of array ‘type name’ is negative

在Clang中它更好一点:

error: array size is negative
  GENERIC_ASSERT(2 + 2 == 5);
  ^~~~~~~~~~~~~~~~~~~~~~~~~~
note: expanded from:
  ((EXPR) ? (void) 0 : assert((EXPR)), (void) sizeof(char[(EXPR) ? 1 : -1]))
                                                          ^~~~~~~~~~~~~~~

答案 1 :(得分:0)

这个怎么样:

#define MY_FUNCTION(X) \
  do{ \
if (X>=1 && X<=20) {\
COMPILER_ASSERT(X != 0); \
my_function(X);\
}\ // end if block
  // you can use break here too
  else myfunction2(X,__FILE__,__LINE__); \ // this will be the runtime assert
 }while (0);


void myfunction2(int x, const char * file, const int line)
{ 
// print here information
exit(-1);
}

答案 2 :(得分:0)

在P99中,我有这个宏用于静态断言

# if p99_has_feature(c_static_assert)
#  define static_assert _Static_assert
# else
#  define static_assert(EXPR, DIAGSTR)                            \
extern char const p00_compiletime_assert[                         \
 sizeof((void const*[3*(!!(EXPR)) - 1]){                          \
    &p00_compiletime_assert,                                      \
   "static assertion failed: " P99_STRINGIFY(EXPR) ", " DIAGSTR}) \
]
extern char const p00_compiletime_assert[sizeof(void const*[2])];
# endif
#endif

这样做的好处是几乎可以在任何地方使用它,至少在C99中可以使用声明。 (C99允许混合语句和声明)所以这可以在任何类型的函数块中使用,或者在文件范围内使用,前提是EXPR是编译时整数常量。