3

是否可以在 C99 中实现 static_if?

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

我怎样才能STATIC_IF(…)在这里正确实施?取决于COND参数或者应该传递给MACRO1or MACRO2,但是两个宏的参数看起来不同。COND是静态可测试的,例如sizeof (…) > 42.

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

4 回答 4

7

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

您不能传递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;
}

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

Order 是相当重量级的:我假设你可以用更轻、更实际便携的 P99(不熟悉它)做类似的事情。Order 与 GCC 配合得很好,与 Clang 配合得很好(Clang 会因深度递归或长循环而窒息);是标准的,但并非所有编译器都是标准的。

于 2014-03-25T03:21:05.837 回答
2

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

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

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

于 2014-03-19T12:25:15.247 回答
2

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

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

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

 // 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
于 2014-03-19T12:25:23.327 回答
1

如果您可以消除必须坚持使用 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(为真)或 1(为假),并针对指向该数组的指针的类型进行调度,以解析哪个要使用的两个不兼容的功能。

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

于 2016-10-10T16:35:20.760 回答