fmt
fmt source walkthrough: why format_to_n returns more than it writes
Read the bounded-output traits and upstream tests to understand total size, the returned iterator, missing null termination and UTF-8 byte truncation.
What you will learn
- Separate stored output from logical output
- Use a sentinel to expose the contract
- Make the evidence boundary explicit
Before you start
- Basic C++ values, references, strings and build targets
- Ability to distinguish a documented expectation from a measured result
Trace concrete fmt behavior and choose an integration with explicit output, lifetime and verification boundaries.
Key takeaways
- result.size is the full formatted size, not the amount stored.
- format_to_n does not append a null terminator.
- A byte write cap is not an encoding-aware preview or a CPU-work limit.
Separate stored output from logical output
format_to_n returns a format_to_n_result containing an output iterator and the total, untruncated output size. In core.h, vformat_to_n constructs an iterator buffer with fixed_buffer_traits, formats into it, then returns buf.out() and buf.count(). The distinction is deliberate: a four-byte destination does not imply the formatted value is only four bytes long.
The traits object tracks both a write limit and a running count. Its limit function adds the full incoming chunk size to count_, but returns only the portion still permitted by the destination limit. In the pointer specialization, once direct destination capacity is exhausted, the buffer switches to internal storage while continuing the formatting/counting path.
Use a sentinel to expose the contract
The pinned format_to_n test writes decimal 12345 into three positions of a four-character array, leaving a sentinel in the fourth position. It expects size 5, an iterator advanced by 3, and the four-byte sequence 123x. The absence of a null terminator is part of the documented API; passing that array to a C-string consumer would not be justified by this result.
A limit of zero still allows the API to report the logical output length while writing no destination characters. Conversely, a nonzero limit is not a Unicode grapheme boundary. For char-based UTF-8 data, limiting the output to two bytes of a three-byte character can leave an incomplete sequence. A text preview must add its own encoding-aware truncation policy.
Make the evidence boundary explicit
The C++ fixture below demonstrates the public API with a sentinel and exact iterator assertions. The accompanying editorial probe also checks zero-limit output, a three-byte UTF-8 input and a mismatch moved to runtime. These examples are small enough to identify a wrong assumption without flooding memory or relying on remote input.
A second independent JavaScript fixture models chunk accounting only; it is not a C++ parser or a native fmt replacement. Keep this distinction when explaining results. Neither the model nor a bounded native fixture establishes performance for huge widths, custom formatters, exception-disabled builds or every possible output iterator.
Implementation steps
- 1
Initialize a buffer with a visible sentinel.
- 2
Format a value whose output exceeds the limit.
- 3
Check the iterator, full size and untouched sentinel separately.
- 4
Add zero-limit and multibyte cases without treating truncation as valid text.
Copy-ready example
#include <fmt/format.h>
#include <cassert>
#include <string>
int main() {
char out[4] = {'x', 'x', 'x', 'x'};
const auto result = fmt::format_to_n(out, 3, "{}", 12345);
assert(result.out == out + 3);
assert(result.size == 5);
assert(std::string(out, 4) == "123x");
}Frequently asked questions
Why does a three-character write report size five?
The size field counts the complete formatted result. The returned iterator identifies the portion actually written.
Can I safely call strlen on this output?
Not from this API contract. It does not append a null terminator; use a known length or explicitly reserve and write a terminator in your own wrapper.
Sources
- include/fmt/core.hSource checked 2026-09-08
- test/format-test.ccSource checked 2026-09-08
- doc/syntax.mdSource checked 2026-09-08