Sometimes you need to fill an std::string
with characters constructed by a C function. A typical example is this:
constexpr static BUFFERSIZE{256};
char buffer[BUFFERSIZE];
snprint (buffer, BUFFERSIZE, formatstring, value1, value2);
return std::string(buffer);
Notice how we first need to fill a local buffer, and then copy it to the std::string
.
The example becomes more complex if the maximum buffersize is calculated and not necessarily something you want to store on the stack. For example:
constexpr static BUFFERSIZE{256};
if (calculatedBufferSize>BUFFERSIZE)
{
auto ptr = std::make_unique<char[]>(calculatedBufferSize);
snprint (ptr.get(), calculatedBufferSize, formatstring, value1, value2);
return std::string(ptr.get());
}
else
{
char buffer[BUFFERSIZE];
snprint (buffer, BUFFERSIZE, formatstring, value1, value2);
return std::string(buffer);
}
This makes the code even more complex, and if the calculatedBufferSize is larger than what we want on the stack, we essentially do the following:
Since C++17 std::string
has a non-const data()
method, implying that this is the way to manipulate strings. So it seems tempting to do this:
std::string result;
result.resize(calculatedBufferSize);
snprint (result.data(), calculatedBufferSize, formatstring, value1, value2);
result.resize(strlen(result.c_str()));
return result;
My experiments show that the last resize is needed to make sure that the length of the string is reported correctly. std::string::length()
does not search for a nul-terminator, it just returns the size (just like std::vector
does).
Notice that we have much less allocation and copying going on:
To be honest, although it seems to be much more efficient, it also looks very 'un-standard' to me. Can somebody indicate whether this is behavior allowed by the C++17 standard? Or is there another way to have this kind of manipulations in a more efficient way?
Please don't refer to question Manipulating std::string, as that question is about much more dirty logic (even using memset
).
Also don't answer that I must use C++ streams (std::string_stream
, efficient?, honestly?). Sometimes you simply have efficient logic in C that you want to reuse.
Modifying the contents pointed to by data()
is fine, assuming you do not set the value at data() + size()
to anything other than the null character. From [string.accessors]:
charT* data() noexcept;
Returns: A pointer
p
such thatp + i == addressof(operator[](i))
for eachi
in[0, size()]
.Complexity: Constant time.
Remarks: The program shall not modify the value stored at
p + size()
to any value other thancharT()
; otherwise, the behavior is undefined.
The statement result.resize(strlen(result.c_str()));
does look a bit odd, though. std::snprintf
returns the number of characters written; using that value to resize the string would be more appropriate. Additionally, it looks slightly neater to construct the string with the correct size instead of constructing an empty one that is immediately resized:
std::string result(maxlen, '\0');
result.resize(std::max(0, std::snprintf(result.data(), maxlen, fmt, value1, value2)));
return result;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With