是否有与`std :: string`一起使用的`vsnprintf`的类似函数

问题描述 投票:1回答:2

我没有看到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样式不透明分配。

注意:我需要一个不需要第三方库支持的解决方案。

c++ variadic-functions stdstring
2个回答
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;
}

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;
}
© www.soinside.com 2019 - 2024. All rights reserved.