fmt
Inside fmt: typed front ends, type erasure and output buffers
Trace fmt::format through a checked front end into vformat and its buffer, then examine how argument views and dynamic storage change lifetime obligations.
What you will learn
- Follow one concrete call path
- Why erase argument types after checking them?
- Ownership is the boundary that often gets missed
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
- Trace format → vformat → buffer formatting → string conversion at the pinned revision.
- Type erasure reduces duplicated implementation without defining a persistent wire format.
- Argument views and dynamic stores have different, type-dependent ownership behavior.
Follow one concrete call path
In format.h, fmt::format accepts fmt::format_string with a typed argument pack and forwards the format text plus a vargs store to vformat. In format-inl.h, vformat creates a memory_buffer, delegates to detail::vformat_to and converts the accumulated buffer into a std::string. This gives the article an actual source path rather than a generic input-processing-output diagram.
The lower-level formatting path has a special case for the exact two-character format {}. Other strings go through parse_format_string and a format_handler carrying parsing and output contexts. This is one observed optimization branch, not proof that every one-field format avoids parsing. Width, precision, literals and custom formatting can follow other branches.
Why erase argument types after checking them?
The public template sees argument types and can establish a format contract. A type-erased implementation can then share more work across callers instead of instantiating a fully typed implementation for every argument combination. The API documentation demonstrates this pattern for a small logging wrapper, while core.h stores descriptors and argument values or pointers in basic_format_args.
The source includes packed and unpacked argument representations, but those implementation details are not a stable application serialization format. Do not persist their memory layout or treat an internal descriptor as a cross-version protocol. At an application boundary, preserve the semantic fields or a finished string instead of private fmt implementation objects.
Ownership is the boundary that often gets missed
fmt::format_args is a view, and make_format_args takes lvalue references to avoid some temporary-lifetime mistakes. These choices do not make a view own all its inputs. Deferring a formatting operation beyond the lifetime of the argument store or backing strings needs an explicit ownership design. Immediate formatting and asynchronous logging are different use cases.
dynamic_format_arg_store provides storage for some values: string and custom types can be copied into owned nodes, while string views and explicit reference wrappers retain borrowing semantics for their referents. The pinned strings_and_refs test makes the difference visible by mutating a backing character array after insertion. If you enqueue work, copy what must survive or format before enqueueing.
Implementation steps
- 1
Locate the public template in format.h.
- 2
Follow vformat and the exact {} fast path in format-inl.h.
- 3
Inspect argument storage and its lifetime comments in core.h.
- 4
Test copied strings, string views and explicit references separately.
Copy-ready example
{"entry":"fmt::format","sharedImplementation":"vformat","destination":"memory_buffer then std::string","exactFastPath":"{}","format_argsOwnsInputs":false,"dynamicStoreCopiesStringViewsBackingData":false}Frequently asked questions
Does fmt::format_args safely own queued data?
No. It is a view; the argument store and any borrowed backing data must remain valid for the formatting operation.
Does dynamic storage mean every argument is deeply copied?
No. The pinned implementation distinguishes copied strings/custom values from string views and explicit reference wrappers.
Sources
- doc/api.mdSource checked 2026-09-08
- include/fmt/core.hSource checked 2026-09-08
- include/fmt/format.hSource checked 2026-09-08
- include/fmt/format-inl.hSource checked 2026-09-08
- include/fmt/args.hSource checked 2026-09-08
- test/args-test.ccSource checked 2026-09-08