Angenommen, ich habe eine C-Funktion, die eine variable Anzahl von Argumenten akzeptiert: Wie kann ich eine andere Funktion aufrufen, die eine variable Anzahl von Argumenten von innen erwartet und alle Argumente übergibt, die in die erste Funktion eingegangen sind?
Beispiel:
void format_string(char *fmt, ...);
void debug_print(int dbg_lvl, char *fmt, ...) {
format_string(fmt, /* how do I pass all the arguments from '...'? */);
fprintf(stdout, fmt);
}
c
variadic-functions
Vicent Marti
quelle
quelle
Antworten:
Um die Ellipsen weiterzugeben, müssen Sie sie in eine va_list konvertieren und diese va_list in Ihrer zweiten Funktion verwenden. Speziell;
quelle
format_string
wird es auch kaum nützlich sein, da es in-situ Änderungen an fmt vornehmen müsste, was sicherlich auch nicht getan werden sollte. Zu den Optionen gehört das vollständige Entfernen von format_string und die Verwendung von vfprintf. Dies setzt jedoch voraus, was format_string tatsächlich tut, oder format_string gibt einen anderen String zurück. Ich werde die Antwort bearbeiten, um Letzteres zu zeigen.argptr
und die aufgerufene Funktion dieargptr
überhaupt verwendet, ist es nur sicher, sie aufzurufenva_end()
und dann neuva_start(argptr, fmt);
zu starten, um sie neu zu initialisieren. Oder Sie können verwenden,va_copy()
wenn Ihr System dies unterstützt (C99 und C11 erfordern dies; C89 / 90 nicht).Es gibt keine Möglichkeit, (z. B.) printf aufzurufen, ohne zu wissen, wie viele Argumente Sie an es weitergeben, es sei denn, Sie möchten sich auf freche und nicht tragbare Tricks einlassen.
Die allgemein verwendete Lösung ist es , immer eine alternative Form von Vararg Funktionen bereitstellen, so
printf
hatvprintf
das ein nimmtva_list
anstelle der...
. Das...
Versionen sind nur Wrapper um dieva_list
Versionen.quelle
vsyslog
ist nicht POSIX - konform.Variadische Funktionen können gefährlich sein . Hier ist ein sicherer Trick:
quelle
#define callVardicMethodSafely(values...) ({ values *v = { values }; _actualFunction(values, sizeof(v) / sizeof(*v)); })
In großartigem C ++ 0x können Sie verschiedene Vorlagen verwenden:
quelle
Sie können die Inline-Assembly für den Funktionsaufruf verwenden. (In diesem Code gehe ich davon aus, dass die Argumente Zeichen sind).
quelle
Sie können auch Makro versuchen.
quelle
Sie können das Übergeben des Formatierers zwar lösen, indem Sie ihn zuerst im lokalen Puffer speichern, dies muss jedoch gestapelt werden und kann manchmal problematisch sein. Ich habe versucht zu folgen und es scheint gut zu funktionieren.
Hoffe das hilft.
quelle
Ross 'Lösung räumte ein bisschen auf. Funktioniert nur, wenn alle Argumente Zeiger sind. Die Sprachimplementierung muss auch das Löschen des vorherigen Kommas unterstützen, wenn
__VA_ARGS__
es leer ist (sowohl Visual Studio C ++ als auch GCC).quelle
Angenommen, Sie haben eine typische Variadic-Funktion, die Sie geschrieben haben. Da vor dem variadischen mindestens ein Argument erforderlich ist
...
, müssen Sie bei der Verwendung immer ein zusätzliches Argument schreiben.Oder tust du?
Wenn Sie Ihre Variadic-Funktion in ein Makro einschließen, benötigen Sie kein vorhergehendes Argument. Betrachten Sie dieses Beispiel:
Dies ist offensichtlich weitaus praktischer, da Sie nicht jedes Mal das ursprüngliche Argument angeben müssen.
quelle
Ich bin mir nicht sicher, ob dies für alle Compiler funktioniert, aber es hat bisher für mich funktioniert.
Sie können das ... zu inner_func () hinzufügen, wenn Sie möchten, aber Sie brauchen es nicht. Dies funktioniert, weil va_start die Adresse der angegebenen Variablen als Startpunkt verwendet. In diesem Fall geben wir einen Verweis auf eine Variable in func (). Es verwendet also diese Adresse und liest die Variablen danach auf dem Stapel. Die Funktion inner_func () liest aus der Stapeladresse von func (). Es funktioniert also nur, wenn beide Funktionen dasselbe Stapelsegment verwenden.
Die Makros va_start und va_arg funktionieren im Allgemeinen, wenn Sie ihnen eine var als Ausgangspunkt geben. Wenn Sie möchten, können Sie Zeiger auf andere Funktionen übergeben und diese auch verwenden. Sie können ganz einfach Ihre eigenen Makros erstellen. Die Makros müssen lediglich Speicheradressen typisieren. Es ist jedoch ärgerlich, sie für alle Compiler und Aufrufkonventionen zum Laufen zu bringen. Daher ist es im Allgemeinen einfacher, diejenigen zu verwenden, die mit dem Compiler geliefert werden.
quelle