C99的预处理器中的static_if

是否可以在C99中实现static_if?

#define STATIC_IF(COND, ...) \ if (COND) MACRO1(__VA_ARGS__); \ else MACRO2(__VA_ARGS__); 

我怎样才能在这里正确实现STATIC_IF(…) ? 根据COND ,参数应该传递给MACRO1MACRO2 ,但两个宏的参数看起来不同。 COND是静态可测试的,类似sizeof (…) > 42

  • #if COND然后#define STATIC_IF MACRO1 …对我的用例不起作用。
  • 我不能使用编译器特定的解决方案。

这是不可能的,因为sizeof(something)>42的条件对于预处理器来说不是静态的。 预处理器纯粹是文本的(原则上,除了算术)。 它不知道C或类型。

请注意, #if中的条件表达受到严格限制。

但是,您可以使用构建技巧。 例如,您可能有一个独立的程序,如

  // generate-sizeof.c #include  #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 

在您的具体情况下(如果我理解您的评论正确),是的,您可以这样做。

您无法将sizeof传递给预处理器中的任何内容,因为预处理器在类型信息可用之前运行。 幸运的是,你不需要sizeof来计算静态写入列表中的参数数量( XY警报! ),所以这不是障碍。

这是使用Order宏库的一种可能的实现:

 #include  #include  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; } 

(这个简单的案例通过参数的数量来索引列表 – 可能不完全是你想要做的,但足以得到这个想法。订单确实提供了一系列复杂的,非评估的控制结构 – ifcondmatch等。 – 用于更复杂的决策。)

订单非常重要:我假设您可以使用更轻便,更逼真的便携式P99(不熟悉它)做类似的事情。 订单与GCC非常合作,并且与Clang相当好(Clang会在深度递归或长循环中窒息); 是标准的,但不是所有的编译器都是。

我不这么认为,不是你的意思。

但是:我会继续前进,并相信优化编译器会注意到条件始终为真(或错误)并做正确的事情,即优化测试。

您可能需要强制进行一些优化以激发编译器执行此操作。

如果您可以消除必须坚持使用C99的限制,那么自C11以来,对于该语言内置的这个问题有一个更好的解决方案:

 #include  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),并针对指向该数组的指针类型进行调度,以便解析要使用的两个不兼容的函数。

这肯定会在运行时减少为空,因为不仅条件是静态的,而且替代的“执行路径”甚至不会进行类型检查,因此不能首先为它生成代码。