How to Format C++ Containers and Tuples with fmtlib
The fmt library provides automatic formatting for STL containers and tuples by including <fmt/ranges.h>, which activates specialized formatters that iterate over elements and emit comma-separated values enclosed in braces.
The fmtlib/fmt repository treats STL-style containers and tuple-like types as first-class formatting objects. By including the range support header, you enable the library to format vectors, maps, tuples, and custom range types without implementing custom formatter specializations. This functionality is implemented primarily in include/fmt/ranges.h with helper traits defined in include/fmt/std.h.
Enabling Range and Tuple Support
To format containers and tuples, include the ranges header alongside the core formatting header. The core engine in include/fmt/format.h dispatches to specialized formatters defined in include/fmt/ranges.h when it detects types satisfying the range or tuple concepts.
#include <vector>
#include <map>
#include <tuple>
#include <fmt/ranges.h> // Required for container and tuple formatting
Formatting STL Containers
In include/fmt/ranges.h, the fmt::detail::range_formatter class template provides the generic implementation for container formatting. This formatter detects types with begin() and end() members satisfying the range concept.
When you pass a container to fmt::format, the range formatter iterates over elements and emits them using each element's own formatter, separating values with commas and wrapping the sequence in braces.
std::vector<int> v = {1, 2, 3};
fmt::print("vector: {}\n", v); // → vector: {1, 2, 3}
std::map<std::string, double> m = {{"pi", 3.1415}, {"e", 2.718}};
fmt::print("map: {}\n", m); // → map: {("e", 2.718), ("pi", 3.1415)}
Format Specifiers for Containers
The range formatter accepts standard format specifiers such as width, precision, and alignment. These specifications are forwarded to each element's formatter, allowing fine-grained control while maintaining the container structure.
Formatting Tuples and Pair-Like Types
Tuple-like types—including std::tuple, std::pair, and std::array—are handled through a separate specialization in include/fmt/ranges.h. The implementation decomposes tuples at compile time using std::tuple_size and std::get, building a list of element formatters via detail::tuple::get_formatters.
The helper trait is_tuple_like, defined in include/fmt/std.h, identifies compatible types by checking for valid std::tuple_size and std::tuple_element specializations.
auto tup = std::make_tuple(42, "answer", 3.14);
fmt::print("tuple: {}\n", tup); // → tuple: {42, answer, 3.14}
Customizing Output with fmt::join
For delimiter control without surrounding braces, use fmt::join, also located in include/fmt/ranges.h. This utility creates a lightweight view that formats a range using a user-specified separator instead of the default ", " and omits the braces.
fmt::print("joined: {}\n", fmt::join(v, "; ")); // → joined: 1; 2; 3
Summary
- Include
<fmt/ranges.h>to enable formatting for any STL container or tuple-like type in fmtlib. - Container formatting is implemented in
include/fmt/ranges.hbyfmt::detail::range_formatter, which iterates over elements usingbegin()andend(). - Tuple formatting relies on
std::tuple_sizeandstd::getwith helper traits defined ininclude/fmt/std.h. - Use
fmt::joinfrominclude/fmt/ranges.hfor custom separators without surrounding braces. - Format specifiers apply to individual elements and are processed recursively by the underlying element formatters.
Frequently Asked Questions
Why do I get compilation errors when formatting vectors without including <fmt/ranges.h>?
The core formatting engine in include/fmt/format.h does not register range formatters by default. You must explicitly include include/fmt/ranges.h to activate the specializations that handle container iteration and tuple decomposition via fmt::detail::range_formatter.
Can I format custom container types that are not part of the STL?
Yes. If your custom type provides begin() and end() member functions satisfying the range concept, the formatter in include/fmt/ranges.h automatically handles it. For tuple-like custom types, ensure your type works with std::tuple_size and std::tuple_element, or specialize the is_tuple_like trait in include/fmt/std.h.
How do I remove the braces when printing a container?
Use fmt::join from include/fmt/ranges.h instead of direct formatting. This function returns a view that formats the range with your specified separator while omitting the surrounding braces and commas.
Does fmtlib support nested containers like vectors of tuples?
Yes. The formatters in include/fmt/ranges.h apply recursively. When encountering nested structures, the library dispatches to detail::tuple::get_formatters for tuple components and range_formatter for container elements, handling arbitrary nesting depths.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →