Fmt: Writing to a char/wchar_t buffer

Created on 14 Sep 2018  路  9Comments  路  Source: fmtlib/fmt

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?

All 9 comments

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.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

vgalka-sl picture vgalka-sl  路  16Comments

jiri picture jiri  路  22Comments

Gachapen picture Gachapen  路  14Comments

christhomas picture christhomas  路  12Comments

abrownsword picture abrownsword  路  15Comments