Comment envelopper une fonction avec des arguments de longueur variable?

Je cherche à le faire en C / C ++.

Je suis tombé sur des arguments de longueur variable, mais cela suggère une solution avec Python & C utilisant libffi .

Maintenant, si je veux envelopper la fonction printf avec myprintf

Ce que je fais est comme ci-dessous:

 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; } 

Mais les résultats ne sont pas comme prévu!

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

N’importe quel point où me manque-t-il?

le problème est que vous ne pouvez pas utiliser ‘printf’ avec va_args. Vous devez utiliser vprintf si vous utilisez des listes d’arguments variables. vprint, vsprintf, vfprintf, etc. (il existe également des versions «sûres» dans l’environnement d’exécution C de Microsoft qui empêchent les dépassements de mémoire tampon, etc.)

Vous échantillonnez fonctionne comme suit:

 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; } 

En C ++ 11, il s’agit d’une solution possible à l’aide des Variadic templates :

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

MODIFIER

Comme @rubenvb le fait remarquer, il faut tenir compte de certains compromis. Par exemple, vous générerez du code pour chaque instance, ce qui mènera à un gonflement du code.

Je ne sais pas non plus ce que tu veux dire par pure

En C ++, nous utilisons

 #include  #include  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); ... } 

En fait, il existe un moyen d’appeler une fonction qui n’a pas de version va_list partir d’un wrapper. L’idée est d’utiliser l’assembleur, ne touchez pas les arguments de la stack et remplacez temporairement l’adresse de retour de la fonction.

Exemple pour Visual C x86. call addr_printf appelle 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 } } 

Utilisez-vous C ou C ++? La prochaine version de C ++, C ++ 0x, prendra en charge les modèles variadiques qui apportent une solution à ce problème.

Une autre solution peut être obtenue en surchargeant les opérateurs pour obtenir une syntaxe comme celle-ci:

 void f(varargs va) { BOOST_FOREACH(varargs::iterator i, va) cout << *i << " "; } f(args = 1, 2, 3, "Hello"); 

Pour que cela fonctionne, la classe varargs doit être implémentée pour remplacer operator = qui renvoie un object proxy qui, à son tour, remplace l' operator , . Cependant, rendre ce type de variante sûr dans le C ++ actuel n'est pas possible pour autant que je sache, car il devrait fonctionner par type d'effacement.

Comment voulez-vous une solution C / C ++ pure?

Le paramètre rest (…) est pris en charge multi-plateforme dans le runtime C.

http://msdn.microsoft.com/en-us/library/kb57fad8.aspx

 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);