cc11

_Generic to fill some union


I want to use the C11 _Generic keyword to fill an union according to the static type, like:

typedef union {
    double d;
    long l;
    const char*s;
    void*p;
} ty;

#define make_ty(X) _Generic((X),                        \
                            double: (ty){.d=(X)},       \
                            long: (ty){.l=(X)},         \
                            const char*: (ty){.s=(X)},  \
                            default: (ty){.p=(X)})

ty from_double(double x) { return make_ty(x); }
ty from_string(const char*s) { return make_ty(s); }
ty from_long(long l) { return make_ty(l);}

but this does not compile, e.g. GCC 5.3 gives (with gcc -std=c11 -Wall):

u.c: In function ‘from_double’:
u.c:11:35: error: incompatible types when initializing type ‘const char *’ 
                  using type ‘double’
              const char*: (ty){.s=(X)}, \
                                   ^
u.c:14:41: note: in expansion of macro ‘make_ty’
       ty from_double(double x) { return make_ty(x); }

BTW, using gcc -std=c99 -Wall gives the same error...

Or is _Generic only useful for tgmath.h ?

I thought that _Generic chooses the expression according to the compiler-known type, so the non-sensical (ty){.s=(x)} would be ignored in from_double....

(if that did work, I would be able to "overload" make_ty according the static, compiler-known, type of the argument...)


Solution

  • All branches of _Generic must be valid code, just as much as in something like if (1) { here; } else { there; }. To have a solution you could take it the other way around. Define functions similar to:

    inline ty from_double(double x) { return (ty){ .d = x }; }
    

    for all your cases and then have the macro as:

    #define make_ty(X) _Generic((X),                     \
                                double: from_double,     \
                                long: from_long,       \
                                ...)(X)
    

    With the visibility through inline compilers are actually able to optimize such code and will usually not pass through calling the function pointer.