2008-09-03 10 views
43

Ich suche dies in C/C++ zu tun.Wie umschließt man eine Funktion mit Argumenten variabler Länge?

Ich stieß auf Variable Length Arguments, aber das schlägt eine Lösung mit Python & C unter Verwendung libffi vor.

Nun, wenn ich wie unter printf Funktion mit myprintf

Was ich tue, ist wickeln will:

void myprintf(char* fmt, ...) 
{ 
    va_list args; 
    va_start(args,fmt); 
    printf(fmt,args); 
    va_end(args); 
} 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    int a = 9; 
    int b = 10; 
    char v = 'C'; 
    myprintf("This is a number: %d and \nthis is a character: %c and \n another number: %d\n",a, v, b); 
    return 0; 
} 

Aber die Ergebnisse nicht wie erwartet!

This is a number: 1244780 and 
this is a character: h and 
another number: 29953463 

Jeder Punkt, wo ich vermisse ??

+2

Die Antwort auf diese Frage ist _very_ anders jetzt, dass C++ 11 aus ist. –

+0

@MooingDuck In der Tat habe ich eine 'Variadic Templates' Antwort hinzugefügt, denkst du, dass es eine schönere Art in C++ 11 gibt? –

+0

@MooingDuck Eine Vararg-Funktion ist keine variable Template-Funktion. Sie unterscheiden sich in Art und Art. – rubenvb

Antwort

63

das Problem ist, dass Sie nicht 'printf' mit va_args verwenden können. Sie müssen vprintf verwenden, wenn Sie variable Argumentlisten verwenden. vprint, vsprintf, vfprintf usw. (es sind auch eine 'sichere' Versionen in Microsoft C-Laufzeit, die Pufferüberläufe verhindern, etc.)

Sie Probe funktioniert wie folgt:

void myprintf(char* fmt, ...) 
{ 
    va_list args; 
    va_start(args,fmt); 
    vprintf(fmt,args); 
    va_end(args); 
} 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    int a = 9; 
    int b = 10; 
    char v = 'C'; 
    myprintf("This is a number: %d and \nthis is a character: %c and \n another number: %d\n",a, v, b); 
    return 0; 
} 
+0

Kannst du etwas darüber sagen, warum du 'printf' nicht mit' va_args' verwenden kannst? Warum 'vprintf'? –

+1

@JohnStrood Um dies direkt zu beantworten: Da printf() kein 'va_list' als Argument akzeptiert, erwartet es eine variable Anzahl von Argumenten (z. B. "..."), die unterschiedlich ist. Siehe die man-Seite für printf() und vprintf(). Nein, wo heißt printf() akzeptiert 'va_list' als Argument, nur eine variable Anzahl von Argumenten des Typs, den die% Format-Codes erwarten (int, long, float, etc). Nur die Funktionsfamilie vprintf() akzeptiert eine va_list. – erco

1

Sind Sie mit C oder C++? Die nächste C++ - Version, C++ 0x, unterstützt variadic templates, die eine Lösung für dieses Problem bieten.

Eine andere Lösung kann eine Überlastung durch geschickte Bediener erreicht werden, um eine Syntax wie folgt zu erreichen:

void f(varargs va) { 
    BOOST_FOREACH(varargs::iterator i, va) 
     cout << *i << " "; 
} 

f(args = 1, 2, 3, "Hello"); 

Um diese Arbeit zu bekommen, die Klasse varargs umgesetzt werden muss operator = außer Kraft zu setzen, die einen Proxy-Objekt zurückgibt welches wiederum operator , überschreibt. Es ist jedoch meines Wissens nicht möglich, diesen Variantentyp in C++ zu sichern, da er typenlöschbar wäre.

+1

C++ 03 könnte Verwenden Sie 'boost :: tuple', das Möglichkeiten bietet, das Obige sicher auszuführen. –

7

Ich bin auch nicht sicher, was Sie durch reine bedeuten

In C++ verwenden wir

#include <cstdarg> 
#include <cstdio> 

class Foo 
{ void Write(const char* pMsg, ...); 
}; 

void Foo::Write(const char* pMsg, ...) 
{ 
    char buffer[4096]; 
    std::va_list arg; 
    va_start(arg, pMsg); 
    std::vsnprintf(buffer, 4096, pMsg, arg); 
    va_end(arg); 
    ... 
} 
0
void myprintf(char* fmt, ...) 
{ 
    va_ list args; 
    va_ start(args,fmt); 
    printf(fmt,args); ----> This is the fault. vprintf(fmt, args); should have been used. 
    va_ end(args); 
} 
If you're just trying to call printf, 
there's a printf variant called vprintf that takes 
the va_list directly : vprintf(fmt, args); 
9

In C++ 11 Dies ist eine mögliche Lösung mittels Variadic templates:

template<typename... Args> 
void myprintf(const char* fmt, Args... args) 
{ 
    std::printf(fmt, args...) ; 
} 

EDIT

Wie @rubenvb darauf hinweist, gibt es Kompromisse zu berücksichtigen, zum Beispiel werden Sie Code für jede Instanz generieren, die zu Code Bloat führt.

2

Eigentlich gibt es eine Möglichkeit, eine Funktion, die keine va_list Version von einem Wrapper hat aufrufen.Die Idee ist, Assembler zu verwenden, Argumente im Stapel nicht zu berühren und die Rückgabeadresse der Funktion temporär zu ersetzen.

Beispiel für Visual C x86. call addr_printf Anrufe printf():

__declspec(thread) static void* _tls_ret; 

static void __stdcall saveret(void *retaddr) { 
    _tls_ret = retaddr; 
} 

static void* __stdcall _getret() { 
    return _tls_ret; 
} 

__declspec(naked) 
static void __stdcall restret_and_return_int(int retval) { 
    __asm { 
     call _getret 
     mov [esp], eax ; /* replace current retaddr with saved */ 
     mov eax, [esp+4] ; /* retval */ 
     ret 4 
    } 
} 

static void __stdcall _dbg_printf_beg(const char *fmt, va_list args) { 
    printf("calling printf(\"%s\")\n", fmt); 
} 

static void __stdcall _dbg_printf_end(int ret) { 
    printf("printf() returned %d\n", ret); 
} 

__declspec(naked) 
int dbg_printf(const char *fmt, ...) 
{ 
    static const void *addr_printf = printf; 
    /* prolog */ 
    __asm { 
     push ebp 
     mov ebp, esp 
     sub esp, __LOCAL_SIZE 
     nop 
    } 
    { 
     va_list args; 
     va_start(args, fmt); 
     _dbg_printf_beg(fmt, args); 
     va_end(args); 
    } 
    /* epilog */ 
    __asm { 
     mov esp, ebp 
     pop ebp 
    } 
    __asm { 
     call saveret 
     call addr_printf 
     push eax 
     push eax 
     call _dbg_printf_end 
     call restret_and_return_int 
    } 
} 
+1

Ich würde es nicht einmal wagen, dies zu schreiben, aber ich kann es nicht bewundern. –

Verwandte Themen