2016-04-08 10 views
2

Ich möchte Funktion Überladung in C durchführen, aber ich versuche, meinen Code auf einem Unix-Server, der nicht C11-Unterstützung daher das _Generic Schlüsselwort ist nicht verfügbar.Funktion Überladung in C ohne _Generic

(Aktualisieren des Servers, so dass es eine neuere Version von GCC hat, ist keine Option).

Gibt es Alternativen zur Verwendung von _Generic, um eine effektive Funktionsüberlastung in C zu simulieren?

+0

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. –

+0

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

+2

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. –

Antwort

3

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.

2

Sie können eine eingeschränkte Form der Überlastung, für einige Argumenttypen tun, etwa so:

void func_int(int); 
void func_long(long); 
void func_longlong(long long); 

#define FUNC(X) \ 
    (sizeof(X) <= sizeof(int) ? func_int(X) \ 
    : sizeof(X) == sizeof(long) ? func_long(X) \ 
    : func_longlong(X)) 

Dies ermöglicht es Ihnen FUNC(i) zu bedienen und haben sie verschiedene Funktionen aufrufen. Es ist begrenzt, weil Sie Typen nur nach ihrer Größe unterscheiden können. Das heißt, wenn sizeof(int) == sizeof(long) dann Sie nie func_long aufrufen werden, und wenn sizeof(long) == sizeof(long long) dann Sie func_longlong nie aufrufen werden. Außerdem können Sie andere Typen wie double nicht überladen, wenn sizeof(double) mit einem der Integertypen identisch ist, für die Sie testen.

Es kann verwendet werden, um z.B. float, double oder long double, wo Sie verschiedene Implementierungen einer Funktion haben können, die in Abhängigkeit von der Genauigkeit (d. H. Anzahl der Bits) im Argumenttyp mehr oder weniger genau berechnen.

0

fand ich eine Methode, die aber ich habe noch ein paar Warnungen erhalten bei der Kompilierung ...

Arbeits Code scheint zu funktionieren:

#include <stdio.h> 

#define print(x)                  \ 
__builtin_choose_expr(__builtin_types_compatible_p(typeof(x), int ), print_int(x) , \ 
__builtin_choose_expr(__builtin_types_compatible_p(typeof(x), char[]), print_string(x), \ 
(void)0)) 

void print_int(int i) { 
    printf("int: %d\n", i); 
} 

void print_string(char* s) { 
    printf("char*: %s\n", s); 
} 

int main(int argc, char* argv[]) { 

    print(1); 
    print("this"); 

    return 0; 
} 

Ausgang:

int: 1 
char*: thing 

Compiler Warnungen:

gcc overload.c -o main 
overload.c: In function 'main': 
overload.c:19: warning: passing argument 1 of 'print_string' makes pointer from integer without a cast 
overload.c:20: warning: passing argument 1 of 'print_int' makes integer from pointer without a cast 
+1

Sie müssen es wie _Generic behandeln. Move (x) außerhalb und die Warnungen werden weggehen. – PSkocik

+0

Was meinst du mit bewegen (x) außerhalb? – tjwrona1992

+0

'#define print (x) \ __builtin_choose_expr (__ builtin_types_compatible_p (typeof (x), int), print_int, \ __builtin_choose_expr (__ builtin_types_compatible_p (typeof (x), char []), print_string, \ (void) 0)) (x) ' – PSkocik

0

Es ist irgendwie möglich, Funktionszeiger und namenlose Struktur innerhalb einer Union zu verwenden. Hier kommt ein Beispiel, in dem wir die Funktionen add und mul überladen. Es gibt zwei Vereinigungen LIBI und LIBF, die namenlose Strukturen enthalten. LIBI enthält die Funktionszeiger add und mulc, die nur ganzzahlige Werte verwenden. LIBF ist das gleiche wie LIBI, außer dass float-Variablen add und multi verwendet werden. Zusätzlich müssen wir addi, muli, addf und mulf-Funktionen außerhalb dieser Vereinigungen erstellen. Funktionszeiger in Unionen werden auf diese 4 Funktionen bezogen. Zum Beispiel wird add in LIBI auf addi verwiesen, weil addi int-Werte verwendet und add in LIBF auf addf verweist, da es nur float-Variablen verwendet. Dieses Beispiel kann auch zum Emulieren eines Namensraums in C verwendet werden, der in der Sprache nicht vorhanden ist. Gewerkschaften verhalten sich in diesem Beispiel wie der Namensraum.

#include<stdio.h> 
#include<stdlib.h> 

union { 
    struct { 
    void (*add)(int *, int); 
    void (*mul)(int *, int); 
    }; 
    }LIBI; 

union { 
    struct { 
    void (*add)(float *, float); 
    void (*mul)(float *, float); 
    }; 
}LIBF; 

void addi(int *a, int c){ 
    *a += c; 
} 

void addf(float *a, float c){ 
    *a += c; 
    } 
void muli(int *a, int c){ 
    *a *= c; 
    } 

void mulf(float *a, float c){ 
    *a *= c; 
    } 

int main(void){ 

LIBI.add = addi; 
LIBF.add = addf; 
LIBI.mul = muli; 
LIBF.mul = mulf; 

int ia = 10; 
int ib = 2; 
float fa = 20.0f; 
float fb = 2.0f; 

LIBI.add(&ia,ib); 
LIBF.add(&fa,fb); 

printf("%d\n",ia); 
printf("%f\n",fa); 

LIBI.mul(&ia,ib); 
LIBF.mul(&fa,fb); 

printf("%d\n",ia); 
printf("%f\n",fa); 

return 0; 
} 
+0

-Code nur Antworten ermutigt arent Da sie für zukünftige Leser nicht viele Informationen liefern, bitte erläutern Sie, was Sie geschrieben haben – WhatsThePoint

Verwandte Themen