Die GCC manual zeigt explizit einen GNU99 (-std=gnu99
) Workaround seit mindestens Version 3.1.1.
Es gibt natürlich Einschränkungen: Alle Varianten müssen den gleichen Rückgabetyp haben, und alle Funktionsvarianten müssen syntaktisch sinnvoll sein. Letzteres ist oft die Ursache für verschiedene Kompilierungsfehler (ungültige Typen für Parameter der Funktionsvariante). Dies kann vermieden werden, indem die Funktionen ohne Parameterprototypen deklariert werden; Allerdings muss man sich daran erinnern, dass dann Standard-Promotion-Aktionen stattfinden (float
werden zu double
hochgestuft und alle Integer-Typen, die kleiner als int
sind, werden zu int
oder unsigned int
hochgestuft). Betrachten Sie dieses Beispielprogramm:
#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; }
Sie müssen den Typ nicht basierend auf einem einzelnen Parameter bestimmen; du kannst z.B. __builtin_types_compatible_p(typeof(x), double) && __builtin_types_compatible_p(typeof(y), double)
zu verifizieren, beide x
und y
sind vom Typ double
.
Wenn kompiliert und ausgeführt, das obige Programm erzeugt folgende Ausgabe
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 }
auf getestet 32-Bit-x86-Linux (ILP32) sowie auf x86-64 (LP64).Und ja, das obige Programm wird Speicher verlieren, da es nie free()
s die dynamisch zugewiesenen Strings von den foo_..()
Funktionsvarianten zurückgegeben.
Nicht wirklich; Sie müssten herausfinden, welche Funktion an jedem Ort aufgerufen werden soll und die richtige Funktion aufrufen, anstatt '_Generic' das mehr oder weniger automatisch für Sie tun zu lassen. Warum können Sie GCC 5.3.0 (sagen wir mal) nicht in '$ HOME/gcc/bin' installieren und dann die Software erstellen? GCC ist nur ein Programm; Wenn Sie die Binärdatei kompilieren und installieren können, die '_Generic' wirklich benötigt, können Sie eine Binärdatei kompilieren und installieren, die' _Generic' unterstützt - selbst wenn niemand auf der Maschine weiß, dass sie da ist. –
Die Version von GCC, die wir verwenden, kam mit der Version von RedHat. Die Unix-Admins sind die einzigen, die es berühren können und der einzige Weg, der passieren wird, ist mit dem nächsten RedHat-Upgrade, das nicht in Kürze erfolgen wird. – tjwrona1992
Unsinn. Sie müssen nicht '/ bin/gcc' oder'/usr/bin/gcc' verwenden; Sie können "ihre" Version von GCC verwenden, um "Ihre" Version von GCC zu erstellen, und dann "Ihren" GCC verwenden, um Ihre Software zu erstellen. Ich mache das die ganze Zeit - auf RHEL-Systemen mit archaischen Compilern, die nicht alle Probleme diagnostizieren, die ein moderner Compiler diagnostizieren kann. –