我没有看到std::string
的构造函数可以消耗va_list
。将va_list
转换为std::string
有一个共同的解决方案吗?
我见过以下形式的解决方案:
std::string vstring (const char * format, ...) {
std::string result;
va_list args;
va_start(args, format);
char buffer[1024];
vsnprintf(buffer, sizeof(buffer), format, args);
result = std::string(buffer);
va_end(args);
return result;
}
这感觉很容易出错和hacky。有没有办法让std::string
直接从va_list
构建或操作?
注意:我对上述解决方案的主要问题是需要猜测我需要的内存量。我不想浪费太多或者没有足够的钱。理想情况下,我想要一个正常工作的
std::string
样式不透明分配。注意:我需要一个不需要第三方库支持的解决方案。
vsnprintf()
可以计算所需的缓冲区大小而无需实际输出到缓冲区,所以你通常根本不需要单独的char[]
,你可以只计算大小,将std::string
分配给那个大小,然后使用std::string
自己的内部缓冲区输出,例如:
std::string vstring (const char * format, ...)
{
std::string result;
va_list args, args_copy;
va_start(args, format);
va_copy(args_copy, args);
int len = vsnprintf(nullptr, 0, format, args);
if (len < 0) {
va_end(args_copy);
va_end(args);
throw std::runtime_error("vsnprintf error");
}
if (len > 0) {
result.resize(len);
// note: &result[0] is *guaranteed* only in C++11 and later
// to point to a buffer of contiguous memory with room for a
// null-terminator, but this "works" in earlier versions
// in *most* common implementations as well...
vsnprintf(&result[0], len+1, format, args_copy); // or result.data() in C++17 and later...
}
va_end(args_copy);
va_end(args);
return result;
}
但是,在C ++ 11之前,使用单独的缓冲区将是更“正确”(即便携式)和更安全的选择,例如:
std::string vstring (const char * format, ...)
{
std::string result;
va_list args, args_copy;
va_start(args, format);
va_copy(args_copy, args);
int len = vsnprintf(nullptr, 0, format, args);
if (len < 0) {
va_end(args_copy);
va_end(args);
throw std::runtime_error("vsnprintf error");
}
if (len > 0) {
std::vector<char> buffer(len+1);
vsnprintf(&buffer[0], buffer.size(), format, args_copy);
result = std::string(&buffer[0], len);
}
va_end(args_copy);
va_end(args);
return result;
}
您可以使用snprintf
可以与nullptr
缓冲区和大小0一起使用以获得结果缓冲区大小并将消息写入std::string
本身的事实。
请注意,如果要重用va_copy
,应使用va_list
。
std::string vformat(const char *format, va_list args)
{
va_list copy;
va_copy(copy, args);
int len = std::vsnprintf(nullptr, 0, format, copy);
va_end(copy);
if (len >= 0) {
std::string s(std::size_t(len) + 1, '\0');
std::vsnprintf(&s[0], s.size(), format, args);
s.resize(len);
return s;
}
const auto err = errno;
const auto ec = std::error_code(err, std::generic_category());
throw std::system_error(ec);
}
std::string format(const char *format, ...)
{
va_list args;
va_start(args, format);
const auto s = vformat(format, args);
va_end(args);
return s;
}