Gibt es eine Möglichkeit, eine Funktionsüberladung in C zu erreichen? Ich betrachte einfache Funktionen, die überladen werden sollen
foo (int a)
foo (char b)
foo (float c , int d)
Ich denke, es gibt keinen direkten Weg; Ich suche nach Problemumgehungen, falls vorhanden.
c
overloading
FL4SOF
quelle
quelle
Antworten:
Es gibt nur wenige Möglichkeiten:
quelle
Ja!
In der Zeit seit dieser Frage hat Standard C (keine Erweiterungen) effektiv gewonnen Unterstützung für die Funktion Überlastung (nicht Betreiber), dank der Zugabe des
_Generic
Schlüsselwort in C11. (wird in GCC seit Version 4.9 unterstützt)(Überladen ist nicht wirklich "eingebaut" in der in der Frage gezeigten Weise, aber es ist kinderleicht, etwas zu implementieren, das so funktioniert.)
_Generic
ist ein Operator zur Kompilierungszeit in derselben Familie wiesizeof
und_Alignof
. Es ist in Standardabschnitt 6.5.1.1 beschrieben. Es werden zwei Hauptparameter akzeptiert: ein Ausdruck (der zur Laufzeit nicht ausgewertet wird) und eine Typ- / Ausdruckszuordnungsliste, die ein bisschen wie einswitch
Block aussieht ._Generic
Ruft den Gesamttyp des Ausdrucks ab und "schaltet" ihn ein, um den Endergebnisausdruck in der Liste für seinen Typ auszuwählen:Der obige Ausdruck
2
ergibt - der Typ des steuernden Ausdrucks istint
, also wählt er den Ausdruck, derint
als Wert zugeordnet ist. Nichts davon bleibt zur Laufzeit übrig. (Dasdefault
Klausel ist optional: Wenn Sie sie weglassen und der Typ nicht übereinstimmt, wird ein Kompilierungsfehler verursacht.)Dies ist nützlich für das Überladen von Funktionen, da es vom C-Präprozessor eingefügt werden kann und einen Ergebnisausdruck basierend auf dem Typ der an das steuernde Makro übergebenen Argumente auswählt. Also (Beispiel aus dem C-Standard):
Dieses Makro implementiert eine überladene
cbrt
Operation, indem es den Typ des Arguments an das Makro weiterleitet, eine geeignete Implementierungsfunktion auswählt und dann das ursprüngliche Makroargument an diese Funktion übergibt.Um Ihr ursprüngliches Beispiel zu implementieren, können wir Folgendes tun:
In diesem Fall hätten wir eine
default:
Zuordnung für den dritten Fall verwenden können, aber das zeigt nicht, wie das Prinzip auf mehrere Argumente ausgedehnt werden kann. Das Endergebnis ist, dass Siefoo(...)
in Ihrem Code verwenden können, ohne sich (viel [1]) über die Art seiner Argumente Gedanken zu machen.In komplizierteren Situationen, z. B. Funktionen, die eine größere Anzahl von Argumenten überladen oder Zahlen variieren, können Sie Dienstprogrammmakros verwenden, um automatisch statische Versandstrukturen zu generieren:
( Umsetzung hier ) Mit etwas Aufwand können Sie die Anzahl der Boilerplates so reduzieren, dass sie einer Sprache mit nativer Unterstützung für Überladung ähneln.
Abgesehen davon war es bereits möglich , die Anzahl der Argumente (nicht den Typ) in C99 zu überladen .
[1] Beachten Sie, dass die Art und Weise, wie C Typen bewertet, Sie jedoch möglicherweise stolpern lässt. Dies wählt aus,
foo_int
wenn Sie beispielsweise versuchen, ein Zeichenliteral zu übergeben, und Sie müssen ein wenig herumspielen, wenn Ihre Überladungen Zeichenfolgenliterale unterstützen sollen. Trotzdem insgesamt ziemlich cool.quelle
Wie bereits erwähnt, wird Überladung in dem Sinne, wie Sie meinen, von C nicht unterstützt. Eine gängige Redewendung zur Lösung des Problems besteht darin, dass die Funktion eine markierte Vereinigung akzeptiert . Dies wird durch einen
struct
Parameter implementiert , bei dem derstruct
selbst aus einer Art Typindikator besteht, z. B. einemenum
und einemunion
der verschiedenen Wertetypen. Beispiel:quelle
whatever
s in getrennte Funktionen (set_int
,set_float
usw.). Dann wird "Markieren mit dem Typ" zu "Hinzufügen des Typnamens zum Funktionsnamen". Die Version in dieser Antwort beinhaltet mehr Eingabe, mehr Laufzeitkosten, mehr Wahrscheinlichkeit von Fehlern, die beim Kompilieren nicht abgefangen werden ... Ich sehe überhaupt keinen Vorteil darin, Dinge auf diese Weise zu tun! 16 positive Stimmen?!Hier ist das klarste und prägnanteste Beispiel, das ich gefunden habe, um das Überladen von Funktionen in C zu demonstrieren:
https://gist.github.com/barosl/e0af4a92b2b8cabd05a7
quelle
Wenn Ihr Compiler gcc ist und es Ihnen nichts ausmacht, jedes Mal Hand-Updates durchzuführen, wenn Sie eine neue Überladung hinzufügen, können Sie Makromagie anwenden und das gewünschte Ergebnis in Bezug auf Anrufer erzielen. Es ist nicht so schön zu schreiben ... aber es ist möglich
Schauen Sie sich __builtin_types_compatible_p an und definieren Sie damit ein Makro, das so etwas tut
aber ja böse, einfach nicht
BEARBEITEN : C1X wird Unterstützung für generische Typausdrücke erhalten, die so aussehen:
quelle
Mehr oder weniger.
Hier gehen Sie mit gutem Beispiel voran:
Es wird 0 und hallo .. von printA und printB ausgegeben.
quelle
Der folgende Ansatz ähnelt dem von a2800276 , jedoch mit etwas C99- Makromagie :
quelle
Dies hilft möglicherweise überhaupt nicht, aber wenn Sie clang verwenden, können Sie das überladbare Attribut verwenden. Dies funktioniert auch beim Kompilieren als C.
http://clang.llvm.org/docs/AttributeReference.html#overloadable
Header
Implementierung
quelle
In dem Sinne, wie du meinst - nein, das kannst du nicht.
Sie können eine
va_arg
Funktion wie deklarierenvoid my_func(char* format, ...);
Sie müssen jedoch im ersten Argument Informationen über die Anzahl der Variablen und ihre Typen übergeben - wie dies auch der
printf()
Fall ist.quelle
Normalerweise wird eine Warze zur Angabe des Typs an den Namen angehängt oder vorangestellt. Sie können in einigen Fällen mit Makros davonkommen, aber es hängt eher davon ab, was Sie versuchen zu tun. Es gibt keinen Polymorphismus in C, nur Zwang.
Einfache generische Operationen können mit Makros ausgeführt werden:
Wenn Ihr Compiler typeof unterstützt , können kompliziertere Operationen in das Makro eingefügt werden. Sie können dann das Symbol foo (x) verwenden, um dieselbe Operation für verschiedene Typen zu unterstützen, aber Sie können das Verhalten zwischen verschiedenen Überladungen nicht variieren. Wenn Sie tatsächliche Funktionen anstelle von Makros möchten, können Sie den Typ möglicherweise in den Namen einfügen und mit einem zweiten Einfügen darauf zugreifen (ich habe es nicht versucht).
quelle
Leushenkos Antwort ist wirklich cool - nur: Das
foo
Beispiel wird nicht mit GCC kompiliert, das bei fehlschlägtfoo(7)
und über dasFIRST
Makro und den tatsächlichen Funktionsaufruf stolpert ((_1, __VA_ARGS__)
wobei ein Überschusskomma verbleibt. Außerdem sind wir in Schwierigkeiten, wenn wir zusätzliche Überladungen bereitstellen möchten , wie zfoo(double)
.Deshalb habe ich mich entschlossen, die Antwort etwas weiter auszuarbeiten, einschließlich einer Überlastung der Leere (
foo(void)
- was einige Probleme verursachte ...).Die Idee ist jetzt: Definieren Sie mehr als ein Generikum in verschiedenen Makros und lassen Sie das richtige entsprechend der Anzahl der Argumente auswählen!
Die Anzahl der Argumente ist aufgrund dieser Antwort recht einfach :
Das ist schön, wir lösen entweder
SELECT_1
oderSELECT_2
(oder mehr Argumente, wenn Sie sie wollen / brauchen) auf, also brauchen wir einfach geeignete Definitionen:OK, ich habe die Leerenüberladung bereits hinzugefügt - diese wird jedoch nicht vom C-Standard abgedeckt, der keine leeren variadischen Argumente zulässt, dh wir verlassen uns dann auf Compiler-Erweiterungen !
Zunächst erzeugt ein leerer Makroaufruf (
foo()
) immer noch ein Token, aber ein leeres. Das Zählmakro gibt also auch bei leerem Makroaufruf tatsächlich 1 statt 0 zurück. Wir können dieses Problem "leicht" beseitigen, wenn wir das Komma__VA_ARGS__
bedingt nach setzen , je nachdem , ob die Liste leer ist oder nicht:Das sah einfach aus, aber das
COMMA
Makro ist ziemlich schwer; Zum Glück wird das Thema bereits in einem Blog von Jens Gustedt behandelt (danke, Jens). Grundlegender Trick ist, dass Funktionsmakros nicht erweitert werden, wenn nicht Klammern folgen. Weitere Erklärungen finden Sie in Jens 'Blog. Wir müssen die Makros nur ein wenig an unsere Bedürfnisse anpassen (ich werde kürzere Namen verwenden und weniger Argumente für die Kürze).Und jetzt geht es uns gut ...
Der vollständige Code in einem Block:
quelle
Können Sie nicht einfach C ++ und nicht alle anderen C ++ - Funktionen außer dieser verwenden?
Wenn immer noch kein nur striktes C, dann würde ich stattdessen verschiedene Funktionen empfehlen .
quelle
Versuchen Sie, diese Funktionen so zu deklarieren, als
extern "C++"
ob Ihr Compiler dies unterstützt: http://msdn.microsoft.com/en-us/library/s6y4zxec(VS.80).aspxquelle
Ich hoffe, der folgende Code hilft Ihnen, die Funktionsüberladung zu verstehen
quelle