如何检查参数是否是C预处理器宏中的整数常量表达式?

我正在清理一个现有的C库,无耻地发布它。

预处理器宏NPOT用于在编译时计算给定积分常数表达式的下一个更大的2的幂。 宏通常用于直接初始化。 对于所有其他情况(例如,使用可变参数),存在具有相同function的内联函数。

但是,如果用户传递变量,则算法会扩展为大量的机器代码。 我的问题是: 我可以做些什么来防止用户向我的宏传递除了一个整数常量表达式之外的任何内容?

 #define NPOT(x) complex_algorithm(x) const int c=10; int main(void) { int i=5; foo = NPOT(5); // works, and does everything it should foo = NPOT(c); // works also, but blows up the code extremely foo = NPOT(i); // blows up the code also } 

我已经尝试过的:

  1. 将宏定义为#define NPOT(x) complex_algorithm(x ## u) 。 它仍然可以工作并抛出 – 即使几乎没有帮助 – 变量参数的编译器错误。 除非没有像iu这样的变量……肮脏,危险,不要它。
  2. 文档,对大多数用户不起作用。

您可以使用任何需要常量积分表达式的表达式,然后进行优化。

 #define NPOT(X) \ (1 \ ? complex_algorithm(X) \ : sizeof(struct { int needs_constant[1 ? 1 : (X)]; }) \ ) 

最终你应该将sizeof的结果转换为适当的整数类型,因此返回表达式是你期望的类型。

我在这里使用一个未标记的struct

  • 有一个类型,所以真的没有临时产生
  • 具有唯一类型,以便表达式可以在代码中的任何位置重复,而不会引起冲突
  • 触发使用VLA,从C99开始, struct不允许使用VLA:

结构或联合的成员可以具有除可变修改类型之外的任何对象类型。

我正在使用三元?: 1作为选择表达式,以确保始终对其类型进行求值,但从不作为表达式求值。

编辑:似乎gcc接受struct中的VLA作为扩展,甚至没有警告它,即使我明确说-std=c99 。 这对他们来说真是个坏主意。

对于这样一个奇怪的编译器:)你可以使用sizeof((int[X]){ 0 }) 。 这与以上版本一样“被禁止”,但即使是gcc也会抱怨它。

 #define INTEGRAL_CONST_EXPR(x) ((void) sizeof (struct {int a:(x);}), (x)) 

如果x不是整数常量表达式,则会产生编译错误。

 my_function(INTEGRAL_CONST_EXPR(1 + 2 + 3)); // OK my_function(INTEGRAL_CONST_EXPR(1.0 + 2 + 3)); // compile error 

请注意,此解决方案不适用于初始化静态变量:

 static int a = INTEGRAL_CONST_EXPR(2 + 3); 

会因为表达式而触发编译错误,不是常量表达式。

由于@JensGustedt放入注释,因此位域宽度不能为负,因此不能在此解决方案中使用解析为负整数的积分常量表达式。