3

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 -E I 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?

Tabernakel
  • 131
  • 1
  • 5

3 Answers3

6

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;
}
Frederik Deweerdt
  • 4,943
  • 2
  • 29
  • 31
5

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

too honest for this site
  • 12,050
  • 4
  • 30
  • 52
  • 1
    I guess many indeed instinctively associate it with the preprocessor because its only useful in macro definitions, which are primary preprocessor fodder. Given that it's not, then the observed behaviour makes perfect sense. So to refine the question, do you know if there's a gcc compiler flag to output the source after the compiler replaced the _Generic expressions? – Tabernakel May 27 '15 at 02:22
  • Well, I do not blame you, but the many sites which actually call it that. I was misslead myself, until I wondered where cpp would get the type-information. I just had to double-check where it was located in the syntax of the compiler. For the flag: You might get the RTL or similar, but had to figure out where it was created. It might be easier just to look at the assembler output (I just use objdump for that). Use -O0, otherwise you might not find things where you would expect them (depends heavily on the arch, however). – too honest for this site May 27 '15 at 02:28
1

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.

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631