Как обернуть функцию аргументами переменной длины?
Я хочу сделать это на C/С++.
Я наткнулся на Аргументы переменной длины, но это предлагает решение с Python и C с использованием libffi.
Теперь, если я хочу обернуть printf
функцией с помощью myprintf
Что я делаю, как показано ниже:
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;
}
Но результаты не так ожидаемы!
This is a number: 1244780 and
this is a character: h and
another number: 29953463
Любая точка, где я пропустил?
Ответы
Ответ 1
проблема заключается в том, что вы не можете использовать 'printf' с va_args. Вы должны использовать vprintf, если вы используете списки переменных аргументов. vprint, vsprintf, vfprintf и т.д. (в среде Microsoft C также есть "безопасные" версии, которые предотвратят переполнение буфера и т.д.)
Вы можете работать следующим образом:
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;
}
Ответ 2
В С++ 11 это одно возможное решение, используя Variadic templates
:
template<typename... Args>
void myprintf(const char* fmt, Args... args )
{
std::printf( fmt, args... ) ;
}
ИЗМЕНИТЬ
Как указывает @rubenvb, есть компромиссы для рассмотрения, например, вы будете генерировать код для каждого экземпляра, который приведет к раздуванию кода.
Ответ 3
Я также не уверен, что вы подразумеваете под чистым
В С++ мы используем
#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);
...
}
Ответ 4
На самом деле существует способ вызова функции, которая не имеет версии va_list
из обертки. Идея состоит в том, чтобы использовать ассемблер, не касаться аргументов в стеке и временно заменять возвращаемый адрес функции.
Пример для Visual C x86. call addr_printf
вызывает 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
}
}
Ответ 5
Используете ли вы C или С++? Следующая версия С++, С++ 0x, поддерживает variadic templates, которые обеспечивают решение этой проблемы.
Еще одно решение может быть достигнуто благодаря умной перегрузке оператора для достижения синтаксиса, подобного этому:
void f(varargs va) {
BOOST_FOREACH(varargs::iterator i, va)
cout << *i << " ";
}
f(args = 1, 2, 3, "Hello");
Чтобы это сработало, класс varargs
должен быть реализован для переопределения operator =
, который возвращает прокси-объект, который, в свою очередь, переопределяет operator ,
. Однако сделать этот вариант безопасным в текущем С++ невозможно, насколько мне известно, поскольку он должен работать с стиранием типа.
Ответ 6
Как вы понимаете чисто C/С++-решение?
Параметр rest (...) поддерживает кросс-платформу в среде выполнения C.
http://msdn.microsoft.com/en-us/library/kb57fad8.aspx
Ответ 7
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 a printf variant called vprintf that takes
the va_list directly : vprintf(fmt, args);