fmtlib range_format Options: The Complete Guide to Formatting C++ Containers
The fmtlib library defines six range_format enumerators—disabled, map, set, sequence, string, and debug_string—that determine how C++ containers and ranges are serialized in fmt::format and fmt::print output operations.
The {fmt} library extends modern C++ formatting capabilities beyond primitive types to complex containers through the range_format enumeration declared in include/fmt/ranges.h. This compile-time mechanism inspects type characteristics to determine whether a container should render as a bracketed sequence, a key-value map, or a literal string. Mastering these range_format options in fmtlib allows developers to control exactly how std::vector, std::map, and custom ranges appear when passed to formatting functions.
The Six range_format Enumerators
The enum class range_format located at line 31 of include/fmt/ranges.h defines six distinct formatting strategies. The library automatically selects the appropriate enumerator at compile-time based on the container's interface.
disabled
The disabled enumerator turns off range formatting entirely. When selected, the formatter treats the range as a single opaque object and falls back to the default formatter, typically invoking operator<< if available. Use this mode when you want fmtlib to ignore a container's range-like properties and rely on a custom stream output implementation.
map
The map enumerator targets dictionary-like containers exposing both key_type and mapped_type. Elements format as key: value pairs wrapped in curly braces {}. This applies to std::map, std::unordered_map, and any user-defined type satisfying the key-value interface.
set
The set enumerator handles set-like collections that expose key_type but lack mapped_type. Elements format as a comma-separated list within curly braces {}, matching the mathematical notation for sets. This covers std::set, std::unordered_set, and similar associative containers.
sequence
The sequence enumerator formats generic containers providing begin() and end() iterators as ordered lists wrapped in square brackets []. This is the default fallback for std::vector, std::list, std::deque, and C-style arrays.
string
The string enumerator interprets character ranges—such as std::string or std::vector<char>—as literal text printed directly without surrounding brackets or quotes. This prevents character containers from being formatted as comma-separated integer sequences.
debug_string
The debug_string enumerator functions like string but escapes non-printable characters into hexadecimal notation (\xNN). This mode helps visualize raw byte buffers and non-ASCII content during debugging sessions.
Compile-Time Type Detection
fmtlib selects the appropriate range_format value through template metaprogramming defined in include/fmt/ranges.h. The range_format_kind_ trait, beginning at line 253, inspects the type at compile-time to detect map-like or set-like interfaces before falling back to sequence or string rules. Once determined, the range_formatter class instantiated around line 391 consumes this enum value to emit the correct opening delimiters, separators, and closing brackets. The runtime implementation in src/format.cc assembles the final formatted string according to these specifications.
Practical Usage Examples
The following example demonstrates how fmtlib automatically applies different range_format modes based on container type:
#include <fmt/ranges.h>
#include <map>
#include <set>
#include <vector>
#include <string>
int main() {
std::vector<int> seq = {1, 2, 3};
std::set<int> s = {4, 5, 6};
std::map<int, char> m = {{7, 'a'}, {8, 'b'}};
std::string str = "hello";
fmt::print("Sequence : {}\n", seq); // → [1, 2, 3]
fmt::print("Set : {}\n", s); // → {4, 5, 6}
fmt::print("Map : {}\n", m); // → {7: a, 8: b}
fmt::print("String : {}\n", str); // → hello
// Debug string (use fmt::debug_string for explicit request)
fmt::print("Debug : {}\n", fmt::debug_string(str));
}
Output:
Sequence : [1, 2, 3]
Set : {4, 5, 6}
Map : {7: a, 8: b}
String : hello
Debug : hello
Disabling Range Formatting for Custom Types
To force the disabled mode for a specific type, specialize the fmtlib formatter for that type or provide a custom operator<< that the library will prefer over the generic range formatter. This effectively bypasses the range_format selection logic in include/fmt/format.h and delegates output to your custom implementation.
Summary
- The
range_formatenum ininclude/fmt/ranges.hdefines six formatting modes:disabled,map,set,sequence,string, anddebug_string. - Compile-time detection via
range_format_kind_(line 253) automatically assigns the appropriate format based on container characteristics. - Maps render as
{key: value}, sets as{elements}, and sequences as[elements]. - String modes treat character containers as literal text, with
debug_stringescaping non-printable bytes. - Custom types can opt-out by specializing formatters, triggering the
disabledfallback.
Frequently Asked Questions
Where is range_format defined in the fmtlib source code?
The enum class range_format is declared at line 31 of include/fmt/ranges.h, with the formatting logic implemented in the range_formatter class template around line 391 of the same file. The dispatch mechanism that routes containers to these formatters resides in include/fmt/format.h.
How does fmtlib distinguish between a map and a set?
The library checks for the presence of both key_type and mapped_type type aliases. Containers with both aliases receive range_format::map, while those with only key_type receive range_format::set. This detection occurs within the range_format_kind_ trait at compile-time.
What is the difference between string and debug_string formatting?
Both modes treat character ranges as literal text without brackets, but debug_string escapes non-printable characters (such as null bytes or control characters) into hexadecimal notation (\xNN), whereas string prints characters directly.
Can I force a specific range_format for a custom container?
Yes. You can specialize the fmt::formatter template for your type or influence the range_format_kind_ trait detection. Alternatively, providing a custom operator<< and ensuring the type is not recognized as a range will trigger the disabled mode, allowing complete manual control over output formatting.
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 →