简体   繁体   中英

gcc -E does not expand C11 _Generic expressions

In a C11 library project I have a couple of macro functions that are exposed under a shared macro name using generics, like this:

#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)

They seem to work nicely, but for analytic reasons I'd like to create preprocessed source for some test cases so I can verify that the compiler chose the expected generics replacements. However, when using gcc -EI get half-expanded output like this:

 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);
 ...

I am assuming that _Generic is a preprocessor feature, and therefore expected the generic macros to be fully expanded like this:

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

Is there any way to achieve this using a gcc flag?

I am assuming that _Generic is a preprocessor feature

It's actually not, as described in the C11 draft , it's a primary-expression , (as is an identifier or a string literal ). So it's handled by the C compiler and not the pre-processor.

Regarding the second part of the question:

Is there any way to achieve this using a gcc flag?

You can dump the GIMPLE tree, which is an intermediate representation after the C has been parsed, which will get you something approaching what you're looking for:

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

Then:

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

Which results in:

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

_Generic (C11, 6.5.1.1) apparently requires information about the type of the controlling expression . This obviously is only provided by the compiler, not the preprocessor. So it is part of the compiler (6.5.1 - primary-expression).

I somewhat wonder why this is called a preprocessor feature (not just by you, but may sites!). I suppose as it just makes sense in a macro, as in normal functions the types are already known.

Sidenote (not relevant for the code shown, but important): There may be only one of compatible types in the association list . It will not distinguish between - for instance - int and typdef int MyInt; or const int (actually only at most one is allowed, 6.5.1.1/constraint 2).

I am assuming that _Generic is a preprocessor feature ...

It isn't. It's treated as an operator , and is documented in section 6.5.1.1 of the C11 standard Section 6.5.1 indicates that a generic-selection is a kind of primary-expression .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM