C Makro __VAR_ARGS__. Datentypen der übergebenen Argumente rausfinden?

... komplette Frage anzeigen

2 Antworten

In C ist jedes Objekt nur ein Speicherbereich. Der Compiler weiß, wie die Werte zu interpretieren sind, aber zur Laufzeit hast Du ohne weitere Kennzeichnung verloren.

Eine gängige Kennzeichnung ist, als erstes Element eine ID anzugeben. C garantiert, dass deren Adresse gleich der Adresse der Struktur ist.

Statt Zahlen oder Strings empfehle ich einen Zeiger auf eine Struktur, die dann auch "virtuelle" Funktionen enthalten kann. Das erspart Dir lästige switch Statements.

typedef struct base_s { /* V-Table */
char const *name;
void (*destroy)(struct base_s const *);
} const base_t;

static void destroy_ball(base_t * b);

static base_t Ball_ID[1] =
{ { "Ball", destroy_ball } };

typedef struct {
base_t * vtab;
/* ... */
} Ball;

#define Ball_INIT(x) { Ball_ID, x };
Ball b1 = Ball_INIT(3.14);

Das Makro va_arg() braucht aber trotzdem den richtigen Datentyp, um das nächste Argument zu ermitteln. Üblicherweise wird dessen Größe von der Position des vorigen Elements auf dem Stack abgezogen. Wenn's nicht passt, bekommst Du nur Müll. Das Ganze funktioniert also nur, wenn Ball und Point gleich groß sind:

for (unsigned int i = 0; i < arg_c; ++i)
{
Ball arg = va_arg(vl, Ball); /* oder Point */
arg.vtab->destroy();
}

Bei unterschiedlich großen Elementen müsste man ziemlich übel hacken, um den richtigen Typ zu ermitteln. Portabel wird das höchstwahrscheinlich nicht. Stattdessen würde ich dann Ball* und Point* übergeben, und diese als base_t* verarbeiten. Das sollte problemlos funktionieren:

for (unsigned int i = 0; i < arg_c; ++i)
{
base_t *arg = va_arg(vl, base_t*);
assert( arg==Ball_ID || arg==Point_ID );
printf("Bye bye %s\\n", arg->name);
arg->destroy();
}

Aber stelle Dich schon auf ein paar Stunden Fehlersuche ein, wenn Du beim Aufruf von clean_up_helper() ein & vergisst.

Falls Du auf Argumente und Rückgabe by-value verzichten kannst, lassen sich Ball und Point auch als einelementiges Array definieren (so wie ich es bei Ball_ID gemacht habe). Dann wird der Adress-Operator überflüssig, und Du hast eine Fehlerquelle weniger.

Antwort bewerten Vielen Dank für Deine Bewertung
Kommentar von SirPigelton
14.01.2016, 11:49

Uhh, das sind Dinge an die ich so noch gar nicht gedacht habe. Vielen Dank für die Antwort. Ich werde mich dann mal ein wenig weiter damit rumquälen und versuche es umzusetzen :)

0
Kommentar von ralphdieter
26.01.2016, 10:59
printf("*s!\n", "Vielen Dank für den Stern");
0
Antwort bewerten Vielen Dank für Deine Bewertung
Kommentar von GoodbyeKitty1
14.01.2016, 03:19

Kannst Du nicht in "Ball" und "Point" reinschreiben, was sie sind? Ist ja schließlich jeweils ein struct...

0
Kommentar von SirPigelton
14.01.2016, 03:23

Erstmal danke für die Antwort.

Allerdings frage ich mich nun ob ich blind bin, oder ich mich nicht richtig ausgedrückt habe.

Du hast mir nun einen Link geschickt, wo gezeigt wird, wie man erkennen kann, welcher Datentyp sich hinter einer Variable versteckt dank _Generic(C11). Genau das benutzte ich ja auch gerade (siehe mein Quellcode).

Darum geht es aber nicht. Es geht um die VAR ARGS Liste. Genaugenommen um 'var_arg'

0

Was möchtest Du wissen?