Does fmt provide some alternatives for writing (using the Python-like formatting syntax) to a fixed size char/wchar_t buffer (i.e. snprintf (sprintf_s) swprintf (swprintf_s)) instead of returning std::string or std::wstring?
Yes, this can be done with format_to and format_to_n functions (http://fmtlib.net/5.2.0/api.html#output-iterator-support):
#include <fmt/format.h>
int main() {
char buffer[10];
fmt::format_to(buffer, "{}", 42);
}
Doesn't an char(&)[N] decay to a char* pointer without a specific overload for arrays?
So isn't the above unsafe and shouldn't one rather use:
#include <fmt/format.h>
#include <iterator>
int main() {
char buffer[10];
fmt::format_to_n(buffer, std::size(buffer), "{}", 42);
}
Yes, I recommend using format_to_n or format_to with a back_insert_iterator into some container.
I started replacing sprintf_s with std::format_to_n, but I noticed some weird behavior.
I reuse the same buffer to format variables (type, name, value). Next, the formatted variables are outputted to some text file:
char buffer[MAX_PATH];
const auto not_null_buffer = NotNull< const_zstring >(buffer);
for (const auto& [key, value] : m_variable_buffer) {
const VARVisitor visitor(key, buffer, std::size(buffer));
std::visit(visitor, value); // will call fmt::format_to_n
WriteStringLine(not_null_buffer);
}
void VARVisitor::operator()(bool value) const {
if (value) {
fmt::format_to_n(m_buffer, m_buffer_size, "{} {} true", g_var_token_bool, m_key.c_str());
}
else {
fmt::format_to_n(m_buffer, m_buffer_size, "{} {} false", g_var_token_bool, m_key.c_str());
}
}
void VARVisitor::operator()(S32 value) const {
fmt::format_to_n(m_buffer, m_buffer_size, "{} {} {}", g_var_token_S32, m_key.c_str(), value);
}
The text file should look like (which was the case with sprintf_s):
S32 anti-aliasing 0
S32 refresh 1
S32 resolution 5
bool vsync false
bool windowed true
but I obtain:
S32 anti-aliasing 0
S32 refresh 1sing 0
S32 resolution 5g 0
bool vsync falseg 0
bool windowed true0
So somehow the null-terminating character is misplaced, since the first one leaks through.
format_to_n doesn't null-terminate. Its API is more similar to std::copy_n than to sprintf. Instead it returns an iterator past the end so you can add a terminating null yourself or treat the result as sized range.
How to properly null-terminate the result of the format_to (without _n)? Memory buffer size() returns the exact size of the resulting string. How can I be sure that the next character still belongs to the memory acquired by the buffer so that I can write terminating character there without stomping anything?
Just push back '\0'.
Just push back '\0'.
So the buffer will _always_ have room for this additional 0 after the string contents?
push_back will increase the size if necessary, same as with all other containers.