我正在尝试为像printf这样的C风格的可变参数函数编写一个包装器,这会增加一些额外的参数,但是我遇到了麻烦:
void printf(char* fmt, ...); // the thing I'm trying to wrap
void wrapper(char* fmt, ...)
{
printf(fmt, extra_arg1, extra_arg2, /* the variadic arguments */);
}
但是我为/* the variadic arguments */
写了什么?
即使我试图包装的函数有一个版本需要va_list
,我也不能这样做:
void vprintf(char* fmt, va_list args);
void wrapper(char* fmt, ...)
{
va_list args;
va_start(args, fmt);
vprintf(fmt, extra_arg1, extra_arg2, args);
va_end(args);
}
extra_arg1
,extra_arg2
和args
不会神奇地变成va_list
所期望的vprintf
。
我知道我可以写一个宏并使用__VA_ARGS__
:
void printf(char* fmt, ...); // the thing I'm trying to wrap
#define wrapper(fmt, ...) printf(fmt, extra_arg1, extra_arg2, __VA_ARGS__)
但我试图避免这种情况,并将包装器作为函数编写。有没有办法做到这一点?
(顺便说一下,我也不能使用C ++ 11可变参数模板。)
答案 0 :(得分:0)
我认为使用宏没有任何问题,但那只是我:)
无论如何,有一种方法可以做到这一点。 如果您不关心可移植性,可以使用内联汇编。 变量函数使用cdecl调用convension,这意味着调用者负责将参数推送到堆栈而不是在被调用者返回后清理堆栈。 因此,一个简单的printf调用
printf("%d, %d, %d", 1, 2, 3)
看起来像:
char format[] = "%d, %d, %d";
__asm {
push 3
push 2
push 1
lea eax, [format]
push eax
call printf
add esp,10h
}
请注意,va_start
使用推送的最后一个参数来获取堆栈上的地址,而va_arg
只是遍历参数。
您可以使用相同的行为并再次推送可变参数:
char new_format[] = "%d, %d, %d, %d, %d";
wrapper(new_format, ...) {
char *va = addressof(new_format)+sizeof(char*); // first argument after new_format
int i;
__asm {
push extra2
push extra1
}
for(i=0; i < num_of_arguments; i++)
__asm push, va += sizeof(argument)
// we just pushed [3, 2, 1] in our example
__asm {
lea eax, [new_format]
push eax
call printf
add esp,18h // <-- We now have to discard more stuff on the stack
}
}
请注意,您必须事先知道堆栈上有多少个参数以及每个参数的大小(printf从格式字符串中计算出来)。
你可以模仿printf并从格式字符串(或你正在包装的任何其他函数)中提取相关信息。
现在,如果您对类型没有任何了解,您仍然可以逃脱它 通过在堆栈上为变量腾出空间并复制内存(从堆栈到堆栈):
__asm {
push extra2
push extra1
sub esp, total-bytes-needed
}
memcpy(esp, block-of-memory, size-of-block);
这是相当麻烦且不便于携带的解决方案,但应该可行。
希望有所帮助!