gcc -E不扩展C11 _Generic表达式

时间:2015-05-27 01:46:45

标签: c generics gcc c-preprocessor

在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标志来实现这个目标?

3 个答案:

答案 0 :(得分:4)

  

我假设_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;
}

答案 1 :(得分:2)

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

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

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

答案 2 :(得分:1)

  

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

不是。它被视为运算符,并在C11标准的6.5.1.1节中有说明。第6.5.1节指出泛型选择是一种主要的-expression