在表达式中使用的宏,同时将其参数强制为编译时常量
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) \
)
我正在寻找一种方法来 #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) \
)