Frage

Sagen, dass ich eine C-Funktion haben, die eine variable Anzahl von Argumenten nimmt: Wie kann ich eine andere Funktion aufrufen, die eine variable Anzahl von Argumenten aus dem Inneren davon erwartet, alle Argumente zu übergeben, die in die erste Funktion bekam

?

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);
 }
War es hilfreich?

Lösung

die Ellipsen weitergeben, müssen Sie sie in einen va_list konvertieren und diesen va_list in Ihrer zweiten Funktion zu verwenden. Im Einzelnen;

void format_string(char *fmt,va_list argptr, char *formatted_string);


void debug_print(int dbg_lvl, char *fmt, ...) 
{    
 char formatted_string[MAX_FMT_SIZE];

 va_list argptr;
 va_start(argptr,fmt);
 format_string(fmt, argptr, formatted_string);
 va_end(argptr);
 fprintf(stdout, "%s",formatted_string);
}

Andere Tipps

Es gibt keinen Weg (zB) printf aufzurufen, ohne zu wissen, wie viele Argumente, die Sie daran vorbei sind, es sei denn, Sie in ungezogen und nicht tragbaren Tricks bekommen möchten.

Die allgemein verwendete Lösung ist immer eine alternative Form von Vararg Funktionen zur Verfügung stellen, so printf hat vprintf die eine va_list anstelle des ... nimmt. Die ... Versionen sind nur Wrapper um die va_list Versionen.

Variadische Funktionen können sein gefährlich . Hier ist ein sicherer Trick:

   void func(type* values) {
        while(*values) {
            x = *values++;
            /* do whatever with x */
        }
    }

func((type[]){val1,val2,val3,val4,0});

In herrlichen C ++ 0x Sie variadische Vorlagen verwenden:

template <typename ... Ts>
void format_string(char *fmt, Ts ... ts) {}

template <typename ... Ts>
void debug_print(int dbg_lvl, char *fmt, Ts ... ts)
{
  format_string(fmt, ts...);
}

Sie können die Inline-Assembler für den Funktionsaufruf verwenden. (In diesem Code gehe ich davon die Argumente Zeichen).

void format_string(char *fmt, ...);
void debug_print(int dbg_level, int numOfArgs, char *fmt, ...)
    {
        va_list argumentsToPass;
        va_start(argumentsToPass, fmt);
        char *list = new char[numOfArgs];
        for(int n = 0; n < numOfArgs; n++)
            list[n] = va_arg(argumentsToPass, char);
        va_end(argumentsToPass);
        for(int n = numOfArgs - 1; n >= 0; n--)
        {
            char next;
            next = list[n];
            __asm push next;
        }
        __asm push fmt;
        __asm call format_string;
        fprintf(stdout, fmt);
    }

Sie können Makro auch versuchen.

#define NONE    0x00
#define DBG     0x1F
#define INFO    0x0F
#define ERR     0x07
#define EMR     0x03
#define CRIT    0x01

#define DEBUG_LEVEL ERR

#define WHERESTR "[FILE : %s, FUNC : %s, LINE : %d]: "
#define WHEREARG __FILE__,__func__,__LINE__
#define DEBUG(...)  fprintf(stderr, __VA_ARGS__)
#define DEBUG_PRINT(X, _fmt, ...)  if((DEBUG_LEVEL & X) == X) \
                                      DEBUG(WHERESTR _fmt, WHEREARG,__VA_ARGS__)

int main()
{
    int x=10;
    DEBUG_PRINT(DBG, "i am x %d\n", x);
    return 0;
}

Auch wenn Sie können das Bestehen der Formatierer lösen, indem es in den lokalen Puffer gespeichert wird zuerst, aber das braucht Stapel und irgendwann Problem sein kann zu behandeln. Ich habe versucht, folgende und es scheint gut zu funktionieren.

#include <stdarg.h>
#include <stdio.h>

void print(char const* fmt, ...)
{
    va_list arg;
    va_start(arg, fmt);
    vprintf(fmt, arg);
    va_end(arg);
}

void printFormatted(char const* fmt, va_list arg)
{
    vprintf(fmt, arg);
}

void showLog(int mdl, char const* type, ...)
{
    print("\nMDL: %d, TYPE: %s", mdl, type);

    va_list arg;
    va_start(arg, type);
    char const* fmt = va_arg(arg, char const*);
    printFormatted(fmt, arg);
    va_end(arg);
}

int main() 
{
    int x = 3, y = 6;
    showLog(1, "INF, ", "Value = %d, %d Looks Good! %s", x, y, "Infact Awesome!!");
    showLog(1, "ERR");
}

Hope, das hilft.

Ross' Lösung aufgeräumte ein wenig. Dies funktioniert nur, wenn alle Argumente Zeiger sind. Auch die Sprache Implementierung muss eliding früheren Komma unterstützen, wenn __VA_ARGS__ leer (Visual Studio C ++ und GCC tut) ist.

// pass number of arguments version
 #define callVardicMethodSafely(...) {value_t *args[] = {NULL, __VA_ARGS__}; _actualFunction(args+1,sizeof(args) / sizeof(*args) - 1);}


// NULL terminated array version
 #define callVardicMethodSafely(...) {value_t *args[] = {NULL, __VA_ARGS__, NULL}; _actualFunction(args+1);}

Angenommen, Sie haben eine typische variadische Funktion, die Sie geschrieben haben. Da mindestens ein Argument vor dem variadische einer ... erforderlich ist, müssen Sie immer ein zusätzliches Argument in Verwendung schreiben.

Oder haben Sie?

Wenn Sie Ihre variadische Funktion in einem Makro wickeln, müssen Sie keine vorhergehenden arg. Betrachten Sie folgendes Beispiel:

#define LOGI(...)
    ((void)__android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__))

Das ist offensichtlich viel bequemer ist, da Sie nicht das anfängliche Argument jedes Mal angeben müssen.

Ich bin nicht sicher, ob dies für alle Compiler funktioniert, aber es hat sich für mich bisher gearbeitet haben.

void inner_func(int &i)
{
  va_list vars;
  va_start(vars, i);
  int j = va_arg(vars);
  va_end(vars); // Generally useless, but should be included.
}

void func(int i, ...)
{
  inner_func(i);
}

Sie können hinzufügen, die ... inner_func (), wenn Sie wollen, aber Sie brauchen es nicht. Es funktioniert, weil va_start die Adresse der angegebenen Variablen als Startpunkt verwendet. In diesem Fall werden wir ihm einen Verweis auf eine Variable in func geben (). So verwendet er die Adresse und liest die Variablen danach auf den Stapel. Die inner_func () Funktion aus der Stapeladresse des func liest (). So funktioniert es nur, wenn beide Funktionen das gleiche Stapelsegment verwendet werden.

Die va_start und va_arg Makros arbeiten in der Regel, wenn man sie alle var als Ausgangspunkt geben. Also, wenn Sie möchten, können Sie Verweise auf andere Funktionen übergeben und diese natürlich auch verwenden. Sie können Ihre eigenen Makros einfach genug machen. Alle Makros tun, sind Speicheradressen typisieren. so dass sie für alle Compiler jedoch arbeiten und Aufrufkonventionen ist ärgerlich. So ist es im Allgemeinen einfacher, diejenigen zu verwenden, die mit dem Compiler kommen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top