One option is have temp buffer start some known size then increase it if see it's not enough with vsnprintf. Are there better approach? Thanks
You can use vasprintf(), but that does an unnecessary heap allocation - it's unlikely to be faster on average. Using alloca you can avoid the heap. Or, you can write directly into the string that's returned: NRVO should avoid a copy, and as of C++11 move semantics would limit the cost sans-NRVO to a few pointer swaps.
#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;
}
An simpler and initially faster option would be:
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;
The potential problem is that you're allocating at least 256 characters however short the actual text stored: that could add up and cost you in memory/cache related performance. You might be able to work around the issue using [shrink_to_fit]http://en.cppreference.com/w/cpp/string/basic_string/shrink_to_fit), but the Standard doesn't require it to actually do anything (the requirements are "non binding"). If you end up having to copy to a new exactly-sized string, you might as well have used the local char array.