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

Macro for use in expression while enforcing its arguments to be compile time constants

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

以 compile-time 最小宏为例。行为应该是:

 #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 */

为了引发编译诊断,通常我可以用类似的东西模拟静态断言:

 typedef char ct_check_is_ct_constant[(CTC_A != 0) + 1];

如果 CTC_A 使用编译时间常量(数字)以外的任何东西,则不会编译(或至少会引发一些诊断);但是如果 CTC_A 是一个数字,它总是会成功(考虑到范围)。 但是这个断言只能在 multi-statement 宏中工作,因此不能用作表达式的一部分。

我猜是这样的:

  #define CT_MIN(CTC_A, CTC_B)                   \
         ( <expr check CTC_A>,                   \
           <expr check CTC_B>,                   \
           (CTC_A) < (CTC_B))? (CTC_A) : (CTC_B) \
         )

但我不知道表达式应该是什么样子,也不知道这样的东西是否存在。

有什么想法吗?

背景:

我有大量的常量,来自不太可靠的来源,通过定制 header。这些常量强烈地参数化了我的代码。我想在预处理器和编译期间为此常量实施零足迹检查,以检查我的假设、环境和编写正确代码生成器的技能。

您可以使用 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)                                )

clang 产生的错误信息非常明确:

enumassert.c:32:24: error: expression is not an integer constant expression
    int c = a + CT_MIN(a,4); /* COMPILER DIAGNOSTIC */
                       ^
enumassert.c:17:60: note: expanded from macro 'CT_MIN'
    ( sizeof(struct { enum { must_be_constant_expression = CTC_A } x; }),  \
                                                           ^

此解决方案似乎没有向名称添加任何符号 space。此外,如果您需要处理非整数类型,以稍微不太精确的错误消息为代价,您可以添加这样的转换:

#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)                                )

对于浮点和整数编译时常量的求值,我想我可以使用@chqrlie 的解决方案并稍微升级它。

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

  sizeof(struct { enum { must_be_constant_expression = (int) (!(CTC_A))} x; })

(我不确定一元 ! 是否给出 yield 一个 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)                                          \
     )