Comment envelopper une fonction avec des arguments de longueur variable ?
-
09-06-2019 - |
Question
Je cherche à faire cela en C/C++.
Je suis tombé sur Arguments de longueur variable mais cela suggère une solution avec Python & C en utilisant libffi.
Maintenant, si je veux conclure printf
fonctionner 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 ceux espérés !
This is a number: 1244780 and
this is a character: h and
another number: 29953463
Un moment où ai-je raté ??
La solution
le problème est que vous ne pouvez pas utiliser 'printf' avec va_args.Tu dois utiliser vprintf si vous utilisez des listes d'arguments variables.vprint, vsprintf, vfprintf, etc.(il existe également des versions « sûres » dans le runtime C de Microsoft qui empêcheront les dépassements de tampon, etc.)
Vous échantillonnez les travaux 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;
}
Autres conseils
En C++11, c'est une solution possible en utilisant Variadic templates
:
template<typename... Args>
void myprintf(const char* fmt, Args... args )
{
std::printf( fmt, args... ) ;
}
MODIFIER
Comme @rubenvb le souligne, il y a des compromis à considérer, par exemple, vous générerez du code pour chaque instance, ce qui entraînera une surcharge du code.
Je ne suis pas sûr non plus de ce que tu entends par pur
En C++, nous utilisons
#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);
...
}
En fait, il existe un moyen d'appeler une fonction qui n'a pas va_list
version à partir d'un wrapper.L'idée est d'utiliser l'assembleur, de ne pas toucher aux arguments dans la pile et de remplacer temporairement l'adresse de retour de la fonction.
Exemple pour Visual C x86. call addr_printf
appels 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 C++, C++0x, prendra en charge modèles variadiques qui apportent une solution à ce problème.
Une autre solution de contournement peut être obtenue par une surcharge intelligente des 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 mis en œuvre pour remplacer operator =
qui renvoie un objet proxy qui, à son tour, remplace operator ,
.Cependant, à ma connaissance, rendre ce type de variante sûr dans le C++ actuel n'est pas possible car il devrait fonctionner par effacement de type.
Comment entendez-vous une solution pure C/C++ ?
Le paramètre rest (...) est pris en charge sur plusieurs plates-formes dans le runtime C.
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);