parameter - c++ variadic templates
¿Cómo envolver una función con argumentos de longitud variable? (7)
Estoy buscando hacer esto en C / C ++.
Encontré Argumentos de longitud variable pero esto sugiere una solución con Python & C usando libffi .
Ahora, si quiero ajustar la función printf
con myprintf
Lo que hago es como a continuación:
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;
}
¡Pero los resultados no son los esperados!
This is a number: 1244780 and
this is a character: h and
another number: 29953463
¿En algún punto donde extrañé?
¿Estás usando C o C ++? La próxima versión de C ++, C ++ 0x, admitirá plantillas variadas que proporcionan una solución a ese problema.
Se puede lograr otra solución mediante la sobrecarga inteligente del operador para lograr una sintaxis como esta:
void f(varargs va) {
BOOST_FOREACH(varargs::iterator i, va)
cout << *i << " ";
}
f(args = 1, 2, 3, "Hello");
Para que esto funcione, la clase varargs
debe implementarse para anular operator =
que devuelve un objeto proxy que, a su vez, anula operator ,
. Sin embargo, por lo que sé, hacer que este tipo de variante sea seguro en C ++ actual no es posible, ya que tendría que funcionar por tipo de borrado.
¿Qué quiere decir una solución pura de C / C ++?
El parámetro de reposo (...) es compatible con plataforma cruzada en el tiempo de ejecución de C.
el problema es que no puedes usar ''printf'' con va_args. Debe usar vprintf si está utilizando listas de argumentos variables. vprint, vsprintf, vfprintf, etc. (también hay versiones "seguras" en el tiempo de ejecución de C de Microsoft que evitarán los excesos de memoria, etc.)
Tu muestra funciona de la siguiente manera:
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, esta es una solución posible que utiliza Variadic templates
:
template<typename... Args>
void myprintf(const char* fmt, Args... args )
{
std::printf( fmt, args... ) ;
}
EDITAR
Como @rubenvb señala que hay consideraciones que considerar, por ejemplo, generará código para cada instancia que conducirá a la saturación del código.
También estoy seguro de lo que quieres decir con puro
En C ++ usamos
#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 realidad, hay una manera de llamar a una función que no tiene una versión de va_list
desde un contenedor. La idea es usar ensamblador, no tocar argumentos en la pila, y reemplazar temporalmente la dirección de retorno de la función.
Ejemplo para Visual C x86. call addr_printf
llamadas 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
}
}
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);