C++ 如何用可变长度参数包装函数?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/41400/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to wrap a function with variable length arguments?
提问by prakash
I am looking to do this in C/C++.
我希望在 C/C++ 中做到这一点。
I came across Variable Length Argumentsbut this suggests a solution with Python & C using libffi.
我遇到了Variable Length Arguments但这提出了一个使用libffi 的Python & C 解决方案。
Now, if I want to wrap printf
function with myprintf
现在,如果我想printf
用myprintf
What I do is like below:
我所做的如下所示:
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;
}
But the results are not as expected!
但结果并不如预期!
This is a number: 1244780 and
this is a character: h and
another number: 29953463
Any point where did I miss??
我错过了任何一点?
回答by Mark
the problem is that you cannot use 'printf' with va_args. You must use vprintfif you are using variable argument lists. vprint, vsprintf, vfprintf, etc. (there are also 'safe' versions in Microsoft's C runtime that will prevent buffer overruns, etc.)
问题是您不能将 'printf' 与 va_args 一起使用。如果您使用可变参数列表,则必须使用vprintf。vprint、vsprintf、vfprintf 等(Microsoft 的 C 运行时中也有“安全”版本可以防止缓冲区溢出等)
You sample works as follows:
您的示例工作如下:
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;
}
回答by Shafik Yaghmour
In C++11 this is one possible solution using Variadic templates
:
在 C++11 中,这是一种可能的解决方案,使用Variadic templates
:
template<typename... Args>
void myprintf(const char* fmt, Args... args )
{
std::printf( fmt, args... ) ;
}
EDIT
编辑
As @rubenvb points out there are trade-offs to consider, for example you will be generating code for each instance which will lead to code bloat.
正如@rubenvb 指出的那样,需要考虑权衡,例如,您将为每个实例生成代码,这将导致代码膨胀。
回答by David Sykes
I am also unsure what you mean by pure
我也不确定你所说的纯是什么意思
In C++ we use
在 C++ 中,我们使用
#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);
...
}
回答by basin
Actually, there's a way to call a function that has no va_list
version from a wrapper. The idea is to use assembler, do not touch arguments in stack, and temporary replace the function return address.
实际上,有一种方法可以va_list
从包装器调用没有版本的函数。思路是使用汇编器,不接触栈中的参数,临时替换函数返回地址。
Example for Visual C x86. call addr_printf
calls printf()
:
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
}
}
回答by Konrad Rudolph
Are you using C or C++? The next C++ version, C++0x, will support variadic templateswhich provide a solution to that problem.
你使用 C 还是 C++?下一个 C++ 版本 C++0x 将支持可变参数模板,为该问题提供解决方案。
Another workaround can be achieved by clever operator overloading to achieve a syntax like this:
另一种解决方法可以通过巧妙的运算符重载来实现,以实现如下语法:
void f(varargs va) {
BOOST_FOREACH(varargs::iterator i, va)
cout << *i << " ";
}
f(args = 1, 2, 3, "Hello");
In order to get this to work, the class varargs
has to be implemented to override operator =
that returns a proxy object which, in turn, overrides operator ,
. However, making this variant type safe in current C++ isn't possible as far as I know since it would have to work by type erasure.
为了使其工作,varargs
必须实现该类以覆盖operator =
返回代理对象的代理对象,而代理对象又覆盖operator ,
. 但是,据我所知,在当前的 C++ 中使这种变体类型安全是不可能的,因为它必须通过类型擦除来工作。
回答by Mark Ingram
How do you mean a pure C/C++ solution?
你是什么意思一个纯 C/C++ 解决方案?
The rest parameter (...) is supported cross platform in the C runtime.
在 C 运行时中支持跨平台的其余参数 (...)。
回答by john
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);