Одна из опций - запуск временного буфера некоторого известного размера, а затем его увеличение, если вы видите, что этого недостаточно с vsnprintf
. Есть ли лучший подход? Спасибо
Вы можете использовать vasprintf()
, но это приводит к ненужному выделению кучи - в среднем это вряд ли будет быстрее. Используя alloca
, вы можете избежать кучи. Или вы можете записать непосредственно в возвращаемое string
: NRVO должен избегать копирования, а с C ++ 11 семантика перемещения будет ограничивать стоимость sans-NRVO несколькими заменами указателей.
#include <cstdio>
#include <cstdarg>
#include <alloca.h>
#include <string>
#include <iostream>
std::string stringf(const char* format, ...)
{
va_list arg_list;
va_start(arg_list, format);
// SUSv2 version doesn't work for buf NULL/size 0, so try printing
// into a small buffer that avoids the double-rendering and alloca path too...
char short_buf[256];
const size_t needed = vsnprintf(short_buf, sizeof short_buf,
format, arg_list) + 1;
if (needed <= sizeof short_buf)
return short_buf;
// need more space...
// OPTION 1
std::string result(needed, ' ');
vsnprintf(result.data(), needed, format, arg_list);
return result; // RVO ensures this is cheap
OR
// OPTION 2
char* p = static_cast<char*>(alloca(needed)); // on stack
vsnprintf(p, needed, format, arg_list);
return p; // text copied into returned string
}
int main()
{
std::string s = stringf("test '%s', n %8.2f\n", "hello world", 3.14);
std::cout << s;
}
Более простой и изначально более быстрый вариант:
std::string result(255, ' '); // 255 spaces + NUL
const size_t needed = vsnprintf(result.data(), result.size() + 1,
format, arg_list);
result.resize(needed); // may truncate, leave or extend...
if (needed > 255) // needed doesn't count NUL
vsnprintf(result.data(), needed + 1, format, arg_list);
return result;
Потенциальная проблема заключается в том, что вы выделяете не менее 256 символов, как бы кратко они не сохраняли фактический текст: это может привести к увеличению производительности, связанной с памятью / кэшем. Возможно, вы сможете обойти эту проблему, используя [shrink_to_fit
] http://en.cppreference.com/w/cpp/string/basic_string/shrink_to_fit),, но стандарт не требует, чтобы он действительно что-то делал (требования «не являются обязательными»). Если вам в итоге придется скопировать в новую строку с точным размером, вы могли бы также использовать локальный массив символов.