C Function Overloading

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:

1#define my_printf(format, ...)  printf(format, __VA_ARGS__)
2
3int main()
4{
5  my_printf("%s %s\n", "Hello", "World");
6  my_printf("%s\n", "Bye!");
7  return 0;
8}

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.

 1int foo_1(int a);
 2int foo_2(int a, int b);
 3int foo_3(int a, int b, int c);
 4
 5int main()
 6{
 7  foo(10);         /* calls foo_1(10) */
 8  foo(20, 30);     /* calls foo_2(20, 30) */
 9  foo(40, 50, 60); /* calls foo_3(40, 50, 60) */
10  return 0;
11}

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.

📧 📋 🐘 | 🔔
 

Meine Dokus über:
 
Weitere externe Links zu:
Alle extern verlinkten Webseiten stehen nicht in Zusammenhang mit opengate.at.
Für deren Inhalt wird keine Haftung übernommen.



Wenn sich eine triviale Erkenntnis mit Dummheit in der Interpretation paart, dann gibt es in der Regel Kollateralschäden in der Anwendung.
frei zitiert nach A. Van der Bellen
... also dann paaren wir mal eine komplexe Erkenntnis mit Klugheit in der Interpretation!