我没有看到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;
}
这感觉容易出错且容易出错。有没有办法从std::string
构造va_list
或对其进行直接操作?
注意:以上解决方案的主要问题是需要猜测所需的内存量。我不想浪费太多或资源不足。理想情况下,我希望使用
std::string
样式的不透明分配。注意::我需要一个不需要第三方库支持的解决方案。
答案 0 :(得分:3)
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;
}
答案 1 :(得分:2)
您可以使用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;
}