【问题标题】:gcc -E does not expand C11 _Generic expressionsgcc -E 不扩展 C11 _Generic 表达式
【发布时间】:2015-05-27 01:46:44
【问题描述】:

在 C11 库项目中,我有几个使用泛型在共享宏名称下公开的宏函​​数,如下所示:

#define signum(operand) _Generic( (operand),    \
    unsigned long long:  __signum_i4, unsigned long:  __signum_i3, unsigned int:  __signum_i2, unsigned short: __signum_i1, unsigned char: __signum_i0,     \
    signed long long:    __signum_i4, signed long:    __signum_i3, signed int:    __signum_i2, signed short:   __signum_i1, signed char:   __signum_i0, \
    long double:         __signum_f2, double:         __signum_f1, float:         __signum_f0,  \
    complex long double: __signum_c2, complex double: __signum_c1, complex float: __signum_c0   \
) (operand)

它们似乎工作得很好,但出于分析原因,我想为一些测试用例创建预处理源,以便我可以验证编译器是否选择了预期的泛型替换。但是,当使用 gcc -E 时,我会得到这样的半扩展输出:

 assert(_Generic( (0LL), unsigned long long: __signum_i4, unsigned long: __signum_i3, unsigned int: __signum_i2, unsigned short: __signum_i1, unsigned char: __signum_i0, signed long long: __signum_i4, signed long: __signum_i3, signed int: __signum_i2, signed short: __signum_i1, signed char: __signum_i0, long double: __signum_f2, double: __signum_f1, float: __signum_f0, _Complex long double: __signum_c2, _Complex double: __signum_c1, _Complex float: __signum_c0 ) (0LL) == 0);
 assert(_Generic( (+1LL), unsigned long long: __signum_i4, unsigned long: __signum_i3, unsigned int: __signum_i2, unsigned short: __signum_i1, unsigned char: __signum_i0, signed long long: __signum_i4, signed long: __signum_i3, signed int: __signum_i2, signed short: __signum_i1, signed char: __signum_i0, long double: __signum_f2, double: __signum_f1, float: __signum_f0, _Complex long double: __signum_c2, _Complex double: __signum_c1, _Complex float: __signum_c0 ) (+1LL) == +1);
 ...

我假设 _Generic 是一个预处理器功能,因此希望通用宏可以像这样完全扩展:

assert(__signum_i4(0LL) == 0);
assert(__signum_i4(+1LL) == +1);
assert(__signum_i4(-1LL) == -1);
...

有没有办法使用 gcc 标志来实现这一点?

【问题讨论】:

    标签: c generics gcc c-preprocessor


    【解决方案1】:

    我假设 _Generic 是一个预处理器功能

    实际上不是,如C11 draft 中所述,它是primary-expression,(identifierstring literal 也是如此)。所以它是由 C 编译器而不是预处理器处理的。

    关于问题的第二部分:

    有没有办法使用 gcc 标志来实现这一点?

    您可以转储 GIMPLE 树,它是 C 被解析后的中间表示,它将为您提供接近您正在寻找的东西:

    #include <math.h>
    #include <stdio.h>
    #define cbrt(X) _Generic((X), long double: cbrtl, \
                                  default: cbrt, \
                                  float: cbrtf)(X)
    
    int main(void)
    {
        long double a = 0.0;
        printf("%e\n", cbrt(a));
        return 0;
    }
    

    然后:

    $ gcc -c -fdump-tree-gimple main.c
    

    结果:

    main ()
    {
      long double D.3241;
      int D.3242;
      long double a;
    
      a = 0.0;
      D.3241 = cbrtl (a);
      printf ("%e\n", D.3241);
      D.3242 = 0;
      return D.3242;
    }
    

    【讨论】:

      【解决方案2】:

      _Generic (C11, 6.5.1.1) 显然需要关于控制表达式类型的信息。这显然只由编译器提供,而不是预处理器。所以它是编译器的一部分(6.5.1 - 主表达式)。

      我有点想知道为什么这被称为预处理器功能(不仅是您,还有可能是网站!)。我想因为它在宏中才有意义,因为在普通函数中类型是已知的。

      旁注(与显示的代码无关,但很重要):关联列表中可能只有一种兼容类型。它不会区分 - 例如 - inttypdef int MyInt;const int(实际上最多只允许一个,6.5.1.1/constraint 2)。

      【讨论】:

      • 我猜很多人确实本能地将它与预处理器联系起来,因为它只在宏定义中有用,宏定义是主要的预处理器素材。鉴于它不是,那么观察到的行为是完全合理的。所以为了细化问题,你知道编译器替换_Generic表达式后是否有gcc编译器标志来输出源代码?
      • 好吧,我不怪你,但实际上很多网站都这么称呼它。我自己被误导了,直到我想知道 cpp 从哪里得到类型信息。我只需要仔细检查它在编译器语法中的位置。对于标志:您可能会获得 RTL 或类似的,但必须弄清楚它是在哪里创建的。仅查看汇编程序输出可能更容易(我只是为此使用 objdump)。使用 -O0,否则您可能无法在期望的位置找到东西(不过,很大程度上取决于拱门)。
      【解决方案3】:

      我假设 _Generic 是一个预处理器功能......

      不是。它被视为运算符,并记录在 C11 标准的第 6.5.1.1 节第 6.5.1 节表明 generic-selection 是一种 primary -表达式.

      【讨论】:

        猜你喜欢
        • 2023-03-12
        • 1970-01-01
        • 1970-01-01
        • 2012-08-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多