我找到了一种在C ++中传递可变长度数组的方法。但它失败了'包装'功能在下面的代码中。其实我想在我的项目中包装格式化功能。 我的代码中我做错了什么?
测试代码
#include <iostream>
#include <stdarg.h>
#include <string>
void log(const char* format, ...)
{
va_list argptr;
va_start(argptr, format);
int length = _vscprintf(format, argptr);
char* buf_ = new char [length + 1];
int ret = vsnprintf(buf_, 1000, format, argptr);
if (ret >= 0) {
std::cout << buf_ << std::endl;
}
delete[] buf_;
va_end(argptr);
}
void wrap(const char *format, ...)
{
va_list ap;
va_start(ap, format);
log(format, ap);
va_end(ap);
}
int main()
{
log( "direct = %d", 1);
wrap("wrap = %d", 1);
return 0;
}
结果就在这里。
direct = 1
wrap = 15137088 // what's happen?
答案 0 :(得分:1)
我找到了一种在C ++中传递可变长度数组的方法
这不是一个可变长度的数组,它不是真正惯用的C ++。 ...
是一个可变长度参数列表,可在C中使用。
包装日志函数的最简单合理的方法是可变参数模板,它可以简单地写成:
template <typename... Args>
void wrap(const char *format, Args&&... args) {
log(format, std::forward<Args>(args)...);
}
在日志函数本身中,vsnprintf
返回将被写入的字节数,如果它填充缓冲区的话。因此,您始终可以使用乐观缓冲区大小调用一次,并在必要时增大缓冲区:您不需要非标准_vscprintf
。这看起来像是:
void log(const char* format, ...)
{
va_list argptr;
va_start(argptr, format);
static const size_t DefaultSize = 200;
// pick some value that makes sense ^^ here
char buf[DefaultSize];
int rv = vsnprintf(buf, DefaultSize, format, argptr);
if (rv < 0) {
// we can't return errors with this prototype:
// should it throw?
return;
}
if (rv >= DefaultSize) {
vector<char> dynbuf(rv+1);
rv = vsnprintf(&dynbuf[0], dynbuf.size(), format, argptr);
std::cout << &dynbuf[0] << std::endl;
} else {
std::cout << buf << std::endl;
}
va_end(argptr);
}
另请注意wrap
知道其所有参数的类型,但在调用C风格的可变参数函数log
时,该信息将被丢弃。您可以将Boost.Format视为类型安全的替代方案 - 作为奖励,它将为您管理缓冲区。
答案 1 :(得分:0)
传递一个va_list,其中预期可变数量的争论(x,y,z)是不可行的。
为了实现你想要的,你需要做这样的事情:
void log_args(const char* format, va_list& argptr)
{
// I'm unsure on this... you may possibly need to make a separate
// copy of the va_list to pass in to each of _vscprintf and vsnprintf.
va_list second;
va_copy(second, argptr);
int length = _vscprintf(format, argptr);
char* buf_ = new char [length + 1];
int ret = vsnprintf(buf_, 1000, format, second);
if (ret >= 0) {
std::cout << buf_ << std::endl;
}
delete[] buf_;
}
void log(const char* format, ...)
{
va_list argptr;
va_start(argptr, format);
log_args(argptr);
va_end(argptr);
}
void wrap(const char *format, ...)
{
va_list ap;
va_start(ap, format);
log_args(format, ap);
va_end(ap);
}
在这个例子中,'wrap'和'log'看起来是一样的...但我认为你想在你的真正的包装函数中做一些额外的事情,否则你为什么要问这个问题。
答案 2 :(得分:0)
在c ++ 11中,您可以使用variadic templates
#include <iostream>
void tprintf(const char* format) // base function
{
std::cout << format;
}
template<typename T, typename... Targs>
void tprintf(const char* format, T value, Targs... Fargs) // recursive variadic function
{
for ( ; *format != '\0'; format++ ) {
if ( *format == '%' ) {
std::cout << value;
tprintf(format+1, Fargs...); // recursive call
return;
}
std::cout << *format;
}
}
int main()
{
tprintf("direct = %\n", 1);
tprintf("wrap = %\n", 1);
return 0;
}