The GCC manual explicitly shows a GNU99 (-std=gnu99) workaround since at least version 3.1.1.
There are limitations, of course: all variants must have the same return type, and all function variants must make syntactic sense. The latter is often the cause of various compile errors (invalid types for function variant parameters). That can be avoided by declaring the functions without parameter prototypes; however, one must then remember that default type promotions will then take place (float are promoted to double, and all integer types smaller than int are promoted to int or unsigned int). Consider this example program:
#define _GNU_SOURCE /* for asprintf() */
#include <stdlib.h>
#include <stdio.h>
typedef struct {
double x;
double y;
double z;
double d;
} plane;
static const char *foo_char_array();
static const char *foo_int();
static const char *foo_long();
static const char *foo_double();
static const char *foo_float();
static const char *foo_short();
static const char *foo_plane();
#define foo(x) \
( __builtin_choose_expr( __builtin_types_compatible_p(typeof(x), int), foo_int(x), \
__builtin_choose_expr( __builtin_types_compatible_p(typeof(x), long), foo_long(x), \
__builtin_choose_expr( __builtin_types_compatible_p(typeof(x), short), foo_short(x), \
__builtin_choose_expr( __builtin_types_compatible_p(typeof(x), float), foo_float(x), \
__builtin_choose_expr( __builtin_types_compatible_p(typeof(x), double), foo_double(x), \
__builtin_choose_expr( __builtin_types_compatible_p(typeof(x), plane), foo_plane(x), \
__builtin_choose_expr( __builtin_types_compatible_p(typeof(x), char []), foo_char_array(x), \
(void)0 ))))))) )
int main(void)
{
double d = 1.0;
float f = 2.0f;
short s = 3;
long n = 4L;
plane p = { 5.0, 6.0, 7.0, 8.0 };
printf("foo(9) = %s\n", foo(9));
printf("foo(10L) = %s\n", foo(10L));
printf("foo(11.0f) = %s\n", foo(11.0f));
printf("foo(12.0) = %s\n", foo(12.0));
printf("foo(\"bar\") = %s\n", foo("bar"));
printf("foo(d) = %s\n", foo(d));
printf("foo(f) = %s\n", foo(f));
printf("foo(s) = %s\n", foo(s));
printf("foo(n) = %s\n", foo(n));
printf("foo(p) = %s\n", foo(p));
return EXIT_SUCCESS;
}
static const char *foo_char_array(char x[]) { return "char []"; }
static const char *foo_int(int x) { static char buffer[40]; snprintf(buffer, sizeof buffer, "(int)%d", x); return (const char *)buffer; }
static const char *foo_long(long x) { static char buffer[40]; snprintf(buffer, sizeof buffer, "(long)%ld", x); return (const char *)buffer; }
static const char *foo_float(double x) { static char buffer[40]; snprintf(buffer, sizeof buffer, "%af", x); return (const char *)buffer; }
static const char *foo_double(double x) { static char buffer[40]; snprintf(buffer, sizeof buffer, "%a", x); return (const char *)buffer; }
static const char *foo_short(int x) { static char buffer[40]; snprintf(buffer, sizeof buffer, "(short)%d", x); return (const char *)buffer; }
static const char *foo_plane(plane p) { static char buffer[120]; snprintf(buffer, sizeof buffer, "(plane){ .x=%g, .y=%g, .z=%g, .d=%g }", p.x, p.y, p.z, p.d); return (const char *)buffer; }
You do not need to determine the type based on a single parameter; you can do e.g. __builtin_types_compatible_p(typeof(x), double) && __builtin_types_compatible_p(typeof(y), double) to verify both x and y are of type double.
When compiled and run, the above program will output
foo(9) = (int)9
foo(10L) = (long)10
foo(11.0f) = 0x1.6p+3f
foo(12.0) = 0x1.8p+3
foo("bar") = char []
foo(d) = 0x1p+0
foo(f) = 0x1p+1f
foo(s) = (short)3
foo(n) = (long)4
foo(p) = (plane){ .x=5, .y=6, .z=7, .d=8 }
tested on 32-bit x86 Linux (ILP32), as well as on x86-64 (LP64). And yes, the above program will leak memory, since it never free()s the dynamically allocated strings returned by the foo_..() function variants.
_Genericdo that for you more or less automatically. Why can't you install GCC 5.3.0 (say) in$HOME/gcc/binand then use that to build the software? GCC is just a program; if you can compile and install the binary that really needs_Generic, you can compile and install a binary that provides support for_Generic— even if no-one else on the machine knows that it is there./bin/gccor/usr/bin/gcc; you can use 'their' version of GCC to build 'your' version of GCC and then use 'your' GCC to build your software. I do this all the time — on RHEL systems with archaic compilers that don't diagnose all the problems that a modern compiler can diagnose.P99_GENERICmacro from Jens Gustedt's P99 might be useful. I haven't used it myself, though, and I don't know how much preprocessing overhead it adds.