[英]static_if in C99's preprocessor
是否可以在C99中实现static_if?
#define STATIC_IF(COND, ...) \
if (COND) MACRO1(__VA_ARGS__); \
else MACRO2(__VA_ARGS__);
如何在这里正确实现STATIC_IF(…)
? 根据COND
,参数应该传递给MACRO1
或MACRO2
,但两个宏的参数看起来不同。 COND
是静态可测试的,类似sizeof (…) > 42
。
#if COND
然后#define STATIC_IF MACRO1
...对我的用例不起作用。 在您的具体情况下(如果我理解您的评论正确),是的,您可以这样做。
您无法将sizeof
传递给预处理器中的任何内容,因为预处理器在类型信息可用之前运行。 幸运的是,你不需要sizeof
来计算静态写入列表中的参数数量( XY警报! ),所以这不是障碍。
这是使用Order宏库的一种可能的实现:
#include <stdio.h>
#include <order/interpreter.h>
void oneArg(int a) {
printf("one arg: %d\n", a);
}
void twoArgs(int a, int b) {
printf("two args: %d %d\n", a, b);
}
void threeArgs(int a, int b, int c) {
printf("three args: %d %d %d\n", a, b, c);
}
#define ORDER_PP_DEF_8function_list \
ORDER_PP_CONST(("unused") \
(oneArg) \
(twoArgs) \
(threeArgs))
#define SelectFunction(...) ORDER_PP ( \
8seq_at(8tuple_size(8((__VA_ARGS__))), 8function_list) \
)
#define Overloaded(...) SelectFunction(__VA_ARGS__)(__VA_ARGS__)
int main(void) {
Overloaded(42);
Overloaded(42, 47);
Overloaded(42, 47, 64);
return 0;
}
(这个简单的例子通过参数的数量来索引列表 - 可能不是你想要做的,但足以得到这个想法。订单确实提供了一系列复杂的,非评估的控制结构 - if
, cond
, match
等。 - 用于更复杂的决策。)
订单非常重要:我假设您可以使用更轻便,更逼真的P99(不熟悉它)来做类似的事情。 订单与GCC非常合作,而Clang则非常好(Clang会在深度递归或长循环中窒息); 它是标准的,但不是所有的编译器都是。
我不这么认为,不是你的意思。
但是:我会继续前进,并相信优化编译器会注意到条件始终为真(或错误)并做正确的事情,即优化测试。
您可能需要强制进行一些优化以激发编译器执行此操作。
这是不可能的,因为sizeof(something)>42
的条件对于预处理器来说不是静态的。 预处理器纯粹是文本的(原则上,除了算术)。 它不知道C或类型。
但是,您可以使用构建技巧。 例如,您可能有一个独立的程序,如
// generate-sizeof.c
#include <stdio.h>
#include "foo-header.h"
int main(int argc, char**argv) {
const char* headername = NULL;
if (argc<2)
{ fprintf(stderr, "%s: missing header name\n", argv[0]);
exit(EXIT_FAILURE); };
headername = argv[1];
FILE *fh = fopen(headername, "w");
if (!fh) { perror(headername); exit(EXIT_FAILURE); };
fprintf(fp, "// generated file %s\n", headername);
fprintf(fp, "#define SIZEOF_charptr %d\n", (int) sizeof(char*));
fprintf(fp, "#define SIZEOF_Foo %d\n", (int) sizeof(Foo));
fclose (fp);
}
然后有一个规则就像
generated-sizes.h : generate-sizeof foo-header.h
./generate-sizeof generated-sizes.h
在你的Makefile
等等......
因此,您的构建机制将生成适当的标头。
如果你想要交叉编译,事情会变得很棘手!
然后,您的标题中可能包含#include "generated-sizes.h"
,以及后面的代码
#if SIZEOF_Foo > 42
#error cannot have such big Foo
#endif
如果您可以消除必须坚持使用C99的限制,那么自C11以来,对于该语言内置的这个问题有一个更好的解决方案:
#include <stdio.h>
void f1(float x, double y, float * z) {
printf("inside f1\n");
}
void f2(int x, _Bool * y) {
printf("inside f2\n");
}
#define STATIC_IF(COND, ...) _Generic(&(int[(!!(COND))+1]){ 0 }, \
int(*)[2]: f1, \
int(*)[1]: f2) \
(__VA_ARGS__)
int main(void) {
float fl;
_Bool b;
STATIC_IF(sizeof(double) > 4, 0.0f, 1.0, &fl);
STATIC_IF(sizeof(double) > 128, 16, &b);
}
_Generic
运算符根据类型执行编译时选择。 由于它基于类型进行选择,因此它也是唯一可以接受冲突类型“参数”的语言级表达式,因为它的目的是根据输入解析正确类型的值。
这意味着您可以轻松地使用它来选择具有不兼容签名的两个函数,因为它将完全忽略通过匹配输入而未选择的函数的类型; 参数(应用于_Generic
返回的任何函数)将仅针对成功匹配进行检查。
虽然_Generic
被设计为对类型而不是值进行分派,但是任何整数常量表达式都可以通过将其用作数组的大小而“变成”类型。 所以在上面的宏中我们创建了一个匿名数组(nb这不是 VLA),计数为2(对于true)或1(对于false),并针对指向该数组的指针类型进行调度以解析要使用的两个不兼容的函数。
这肯定会在运行时减少为空,因为不仅条件是静态的,而且替代的“执行路径”甚至不会进行类型检查,因此不能首先为它生成代码。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.