C 将其参数强制为编译时常量时在表达式中使用的宏

C 将其参数强制为编译时常量时在表达式中使用的宏,c,macros,c89,static-assert,compile-time-constant,C,Macros,C89,Static Assert,Compile Time Constant,我正在寻找一种方法来#定义一个宏,该宏将其参数强制为编译时常量,同时可以在表达式中使用。该方法应该在C90下工作,并向上兼容——如果可能,也可移植到不同的C++变体。此外,最好是0-footprint到内存 以编译时最小宏为例。行为应该是: #define CT_MIN(CTC_A, CTC B) <<<MAGIC>>> int a = 1; int b = a + CT_MIN(1,4); /* OK */ int c = a + CT_MIN(a

我正在寻找一种方法来#定义一个宏,该宏将其参数强制为编译时常量,同时可以在表达式中使用。该方法应该在C90下工作,并向上兼容——如果可能,也可移植到不同的C++变体。此外,最好是0-footprint到内存

以编译时最小宏为例。行为应该是:

 #define CT_MIN(CTC_A, CTC B) <<<MAGIC>>>

 int a = 1;
 int b = a + CT_MIN(1,4); /* OK */
 int c = a + CT_MIN(a,4); /* COMPILER DIAGNOSTIC */
如果CTC_a使用编译时常量(数字)以外的任何东西,则不会编译(或至少会引发一些诊断);但是如果CTC_A是一个数字,那么它将总是成功的(考虑到对范围的一些关注)。 但是这个断言只能在多语句宏中工作,因此不能作为表达式的一部分使用

我猜是这样的:

  #define CT_MIN(CTC_A, CTC_B)                   \
         ( <expr check CTC_A>,                   \
           <expr check CTC_B>,                   \
           (CTC_A) < (CTC_B))? (CTC_A) : (CTC_B) \
         )
#定义CT_MIN(CTC_A,CTC_B)\
( ,                   \
,                   \
(反恐委员会A)<(反恐委员会B))?(反恐委员会A):(反恐委员会B)\
)
但是我不知道这些表达式应该是什么样子的,如果真的存在的话

有什么想法吗

背景:


我通过一个costumized头从不太可靠的来源获得了大量常量。这些常量强烈地参数化了我的代码。我想在预处理器和编译时对这些常量执行零占用检查,以检查我的假设、环境和编写正确代码生成器的技能

您可以使用
sizeof
应用于匿名结构,该结构具有一个字段,该字段的类型为本地定义的
enum
,其值必须为常量整数表达式:

#define CT_MIN(CTC_A, CTC_B)                                               \
    ( sizeof(struct { enum { must_be_constant_expression = CTC_A } x; }),  \
      sizeof(struct { enum { must_be_constant_expression = CTC_B } x; }),  \
      (CTC_A) < (CTC_B) ? (CTC_A) : (CTC_B)                                )
此解决方案似乎没有向名称空间添加任何符号。此外,如果您需要处理非整数类型,则可以添加如下类型的强制转换:

#define CT_MIN(CTC_A, CTC_B)                                               \
    ( sizeof(struct { enum { must_be_constant_expression = (int)(CTC_A) } x; }),  \
      sizeof(struct { enum { must_be_constant_expression = (int)(CTC_B) } x; }),  \
      (CTC_A) < (CTC_B) ? (CTC_A) : (CTC_B)                                )
#定义CT_MIN(CTC_A,CTC_B)\
(sizeof(struct{enum{must_be_constant_expression=(int)(CTC_A)}x;})\
sizeof(struct{enum{必须是_常量_表达式=(int)(CTC_B)}x;})\
(反恐委员会A)<(反恐委员会B)?(反恐委员会A:(反恐委员会B))

对于浮点和整数编译时常量的计算,我想我可以使用@chqrlie的解决方案,并对其进行一些升级

检查参数是否为编译时常量的表达式可能是:

  sizeof(struct { enum { must_be_constant_expression = (int) (!(CTC_A))} x; })
(我不确定一元数!是否给出了一个
int
或某种类型的CTC_A)

这应该能处理大部分的事情,一个MIN操作可以在编译时合理地完成

完整的宏是:

 #define CT_MIN(CTC_A, CTC_B)                                                         \
     ( sizeof(struct { enum { must_be_constant_expression = (int) (!(CTC_A)) } x; }), \
       sizeof(struct { enum { must_be_constant_expression = (int) (!(CTC_B)) } x; }), \
       (CTC_A) < (CTC_B) ? (CTC_A) : (CTC_B)                                          \
     )
#定义CT_MIN(CTC_A,CTC_B)\
(sizeof(struct{enum{must_是_常量_expression=(int)(!(CTC_A))}x;})\
sizeof(struct{enum{必须是常数\表达式=(int)(!(CTC_B))}x;})\
(反恐A)<(反恐B)?(反恐A):(反恐B)\
)

注意VLA类型。我认为您可能可以使用一个位字段定义一个结构类型,其大小是根据宏参数定义的,尽管我不相信自己知道所有的注意事项并完全正确。@user2357112:-)我不完全确定如何使用VLA类型来实现一个在编译时会失败的MIN宏,如果它的参数不是编译时常量。我是说确保你不会意外地声明一个VLA类型,认为数组长度总是编译时常量。啊,我明白了。谢谢我并不完全了解VLA的功能(不知怎么的,我仍然在使用C89)。除了答案之外,您能否提供一个不会成为VLA的静态断言?它必须是标准的C89?例如,gcc有一些扩展,可以让您实现这一点。这种方法是否会在名称空间中添加宏使用的内容?还有浮点常量呢?类似于:`sizeof(struct{enum{XXX=(int)(CTC_A)}x;})`?溢出也可能会导致问题,对吗?无符号强制转换(unsigned int)可以在大浮点数上很好地定义吗?@MarkA.:我认为它甚至不会在名称空间中添加符号
must\u constant\u expression
,因为它是在
struct
中定义的,并在下面用不同的值重新定义,而没有任何警告。ok。谢谢如果您能确认answer提供的解决方案的附加组件起到了作用(可能更普遍),您可能希望更新您的答案,那么我愿意接受您的答案。我认为,从大浮点变量进行强制转换是不够的,因为它无法处理溢出。我用了这个!运算符从任何数字生成一个小值-希望如此。
运算符的计算结果实际上是一个值为
0
1
int
。这个解决方案应该像简单cast一样工作,因为某种原因,
clang
抱怨如果参数是double,它就不是编译器时间常数,声称将其折叠为常数是GNU扩展。我不明白为什么,但这会使此解决方案无法使用。@chqrlie请参阅我对您的更新答案的评论。。。这是一个编译器错误吗?我在标准中没有理由…好的。在标准中找到了原因:ANSI/ISO 9899:1990 6.5.2.2枚举说明符:“约束:[……]枚举常量应为整型常量表达式[…]”和6.4常量表达式:[……]整型常量表达式[…]应仅具有[…]类型的操作数sizeof表达式和作为强制转换的直接操作数的浮点常量。[…]”将
sizeof
应用于参数会破坏检查它是否为常量表达式的目的
sizeof(expr)
是一个常量表达式,即使
expr
h
  sizeof(struct { enum { must_be_constant_expression = (int) (!(CTC_A))} x; })
 #define CT_MIN(CTC_A, CTC_B)                                                         \
     ( sizeof(struct { enum { must_be_constant_expression = (int) (!(CTC_A)) } x; }), \
       sizeof(struct { enum { must_be_constant_expression = (int) (!(CTC_B)) } x; }), \
       (CTC_A) < (CTC_B) ? (CTC_A) : (CTC_B)                                          \
     )