C Function Overloading
« | 10 Jul 2022 | »In C kann eine Funktion genau eine Signatur haben, während in C++ unter einem Funktionsnamen mehrere Implementierung möglich sind. Der Compiler adressiert die richtige basierend auf den Datentypen beim Aufruf.
Aber mit ein paar Makro-Tricks, kann man zumindest bei der Anzahl der Parameter ein bisschen C++ Overloading in den alten C Standard übertragen.
Variadic Macros
Makros können so wie auch variadic Funktionen mit einer undefinierten Anzahl von Parametern aufgerufen werden. Das Makro kann aber dann nicht mehr damit tun, als diese Parameter “weiterzuleiten”.
Das klassische Beispiel dafür sind printf
Adaptionen:
Und nun eine verrückte Frage:
Was wäre, wenn ein Makro eine Funktion simuliert und je nach Anzahl der Parameter eine andere Funktion aufruft?
Argumente mit Makros zählen
Man findet im Netz einen interessanten Trick, mit dem man per
Makro die Anzahl der Argumente des Aufrufs zählen kann.
Mit dieser Zahl kann man dann einen Aufruf auf einen zusammengesetzten
Funktionsnamen umleiten, der auf genau diese Zahl endet:
1#define COUNT_ARGS_IMPL(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N,...) N 2#define COUNT_ARGS_HELPER(tuple) COUNT_ARGS_IMPL tuple 3#define COUNT_ARGS(...) COUNT_ARGS_HELPER((__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)) 4 5#define CONCAT(a, b) a ## b 6#define BUILD_FUNCTION(func_prefix, arg_count) CONCAT(func_prefix, arg_count) 7#define CALL_OVERLOADED(func_prefix, args) BUILD_FUNCTION(func_prefix, COUNT_ARGS args) args
COUNT_ARGS
fügt den originalen Argumenten die Zahlen 10 bis 0 hinzu, weil
wir bis zu maximal 10 Argumente unterstützen.
Die damit aufgerufene Funktion COUNT_ARGS_IMPL
schluckt beliebig viele
Parameter und interessiert sich für den 11. Denn dieser verrät uns die Anzahl
der Parameter der originalen Funktion.
Ein Aufruf (a, b, c)
wird umgeformt zu
(a, b, c, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
und an der 11. Stelle steht dann
3
, die von COUNT_ARGS_IMPL
zurückgegeben wird.
Das Makro CALL_OVERLOADED
versucht jetzt ein Funktionsnamen über ein Präfix
und die ermittelten Argumentanzahl zusammenzubauen und ruft sie mit den
Argumenten auf.
Jetzt kann man sich Funktionsaufrufe mit einem Makro umleiten lassen, z.B.:
1#define foo(...) CALL_OVERLOADED(foo_, (__VA_ARGS__))
Wird foo(x, y)
mit 2 Parametern aufgerufen, wird ein foo_2(x, y)
daraus.
Ein vollständiges Beispiel liegt im BLOG-Classroom.
Problem MSVC
Microsofts Präprozessor wurde lange nicht aktualisiert und läuft daher nicht-standard-konform und kann die obigen Makros nicht richtig auflösen.
Doch seit MSVC 2019 (14.2) existiert der Compilerswitch /Zc:preprocessor
mit dem man den Standard-konformen Modus einschalten kann und somit kann
auch Microsoft endlich von den vielen “coolen” Makrospielereien profitieren,
die man so im Netz findet.
Fazit
Ich hatte eigentlich wieder an meinem Lambda-Problem herumexperimentiert und bin dabei über die Frage gestoßen, wie man aus der Anzahl von Makroparametern unterschiedliche Klassennamen generieren kann.
Das hat im Endeffekt mein Problem zwar nicht lösen können, aber die Idee in C etwas Overloading-ähnliches einzubauen, und auszuprobieren, hat wieder Spaß gemacht.
Im GATE Projekt kann ich das nicht auf großer Ebene einsetzen, weil dort auch ältere MSVC Compiler unterstützt werden sollen, aber vielleicht ist der Ansatz ja bei manchen GCC Fixes eine willkommene Option.