8.5    Generische Auswahl

In C++, einer Variante von C, ist es möglich, Funktionen zu überladen, wobei die Funktionen mit verschiedenen Parameterlisten denselben Bezeichner haben dürfen. Anhand des Datentyps des Übergabeparameters erkennt der Compiler dann die entsprechende Funktion. Dies kann praktisch sein, weil so eine Funktion immer mit dem gleichen Namen, aber unterschiedlichem Datentyp aufgerufen und verwendet werden kann. Ein einfaches Beispiel hierzu wäre:

void genfunc_integer(int i) { … }
void genfunc_double(double d) { … }
void genfunc_complex(double complex c) { … }

Ein echtes Überladen kann zwar mit C nicht realisiert werden, aber seit C11 wurde die generische Auswahl (engl. generic selection) eingeführt, die eine ähnliche Funktionalität bietet und die unterschiedlich bezeichnete Funktion anhand des übergebenen Datentyps auswählt. Hierzu wurde das Schlüsselwort _Generic eingeführt. Mit diesem Schlüsselwort können Sie Makros definieren, mit denen Sie eine Fallunterscheidung aufgrund des Datentyps treffen können.

Hier ein einfaches Anwendungsbeispiel, das die Verwendung von _Generic in der Praxis zeigt:

00  // Kapitel8/genfunc.c
01 #include <stdio.h>
02 #include <complex.h>
03 #include <stdlib.h>

04 #define genfunc(X) _Generic ((X), \
default: genfunc_integer, \
double: genfunc_double, \
double complex: genfunc_complex \
)(X)

05 void genfunc_integer(int i) {
06 printf("Integer: %d\n", i);
07 }

08 void genfunc_double(double d) {
09 printf("Double: %lf\n", d);
10 }

11 void genfunc_complex(double complex c) {
12 printf("Complex: %lf %lf\n", creal(c), cimag(c));
13 }

14 int main(void) {
15 int ival = 12345;
16 double dval = 3.14;
17 double complex dc = 2.2 + 3.3 * I;
18 genfunc(ival); // verwendet genfunc_integer
19 genfunc(dval); // verwendet genfunc_double
20 genfunc(dc); // verwendet genfunc_complex
21 return EXIT_SUCCESS;
22 }

Listing 8.8    genfunc.c demonstriert dem Umgang mit generischen Funktionen.

In Zeile (04) können Sie die Definition des Makros genfunc(X) sehen, wobei X der Platzhalter für den zu übergebenden Datentyp ist. Nach dem Schlüsselwort _Generic stehen die Zuweisungen des entsprechenden Datentyps und der entsprechenden Funktion. Die Funktionen selbst werden in den Zeilen (05) bis (13) definiert. In der main()-Funktion, in den Zeilen (18), (19) und (20), sehen Sie nun den Vorteil von _Generic. Hier brauchen Sie nur noch genfunc() zu verwenden, und es wird automatisch die passende Funktion aufgerufen, wenn für den übergebenen Datentyp eine solche existiert. Passt kein einziger Typ zur Ihren Definitionen, wird die Funktion aufgerufen, die hinter default notiert wurde (im letzten Beispiel ist dies die Version für den Typ int).

Das Beispiel gibt bei der Ausführung z. B. Folgendes aus:

Integer: 12345
Double: 3.140000
Complex: 2.200000 3.300000