How to Use fmt::format for String Formatting in Modern C++
Use fmt::format from the fmtlib/fmt library by including <fmt/core.h> and calling the function with a format string containing curly-brace replacement fields, optionally using positional indices, named arguments via fmt::arg(), and format specifiers for width, precision, and alignment.
The fmt library (fmtlib/fmt) provides a modern, type-safe alternative to C's printf and C++ iostreams. The core formatting functionality lives in include/fmt/core.h and include/fmt/format.h, offering compile-time checking and Pythonic syntax. Unlike traditional formatting methods, fmt::format validates format strings and argument types at compile time when possible, catching mismatched arguments or invalid specifiers before your program runs.
Understanding the fmt::format Architecture
The fmt library organizes its functionality across three primary header files. In include/fmt/core.h, you will find the Core API exposing fmt::format, fmt::print, and the fmt::arg helper for named parameters. The parsing machinery for format strings resides in include/fmt/format.h, which contains the template metaprogramming that drives the replacement field logic.
The heavy lifting occurs in either src/format.cc when building as a compiled library, or include/fmt/format-inl.h when using the header-only mode via the FMT_HEADER_ONLY macro. This architecture enables fmt::format to parse format strings at compile time whenever possible, storing the result as static format-string objects that minimize runtime overhead.
Basic fmt::format Syntax and Replacement Fields
At its foundation, fmt::format interleaves ordinary text with replacement fields delimited by { and }. The simplest usage inserts arguments in order using empty braces.
#include <fmt/core.h>
int main() {
std::string s = fmt::format("The answer is {}.", 42);
// s == "The answer is 42."
}
Each {} constitutes a replacement field that consumes the next argument in sequence. The syntax mirrors Python's str.format, as documented in doc/syntax.md, and supports automatic left-to-right indexing for omitted argument identifiers.
Positional and Named Arguments
Explicit Positional Indices
You can reorder arguments using explicit positional indices inside the braces. This is particularly useful when translating strings where word order varies between languages.
std::string s = fmt::format("{1}, {0}!", "world", "hello");
// s == "hello, world!"
Mixing automatic indexing (empty braces) with explicit indices triggers a compile-time error, ensuring consistency in argument handling.
Named Arguments via fmt::arg
For complex formatting scenarios with many parameters, named arguments improve readability. Use the fmt::arg function defined in include/fmt/core.h to bind names to values.
std::string s = fmt::format("{greeting}, {name}!",
fmt::arg("greeting", "hi"),
fmt::arg("name", "fmt"));
// s == "hi, fmt!"
Format Specifiers for Width, Alignment, and Numeric Output
The optional :format_spec section inside replacement fields controls visual presentation. The grammar supports width, precision, alignment, fill characters, sign handling, alternate forms, and type conversion.
Alignment and Fill Characters
Control text positioning using < (left), > (right), and ^ (center) alignment operators, optionally preceded by a fill character.
fmt::print("[{:<10}]\n", "left"); // "[left ]"
fmt::print("[{:>10}]\n", "right"); // "[ right]"
fmt::print("[{:^10}]\n", "center"); // "[ center ]"
fmt::print("[{:*^10}]\n", "star"); // "[***star***]"
Numeric Formatting Flags
Apply sign indicators, alternate forms, and zero-padding to numeric types using specific flags before the type specifier.
fmt::print("{:+d} {:+d}\n", 7, -7); // "+7 -7"
fmt::print("{:#06x}\n", 0xfe); // "0x00fe"
fmt::print("{:08d}\n", 42); // "00000042"
The + flag forces a sign on positive numbers, # triggers the alternate form (such as 0x for hexadecimal), and 0 enables zero-padding up to the specified width.
Precision for Floating-Point and Strings
Precision behaves contextually: for floating-point types it specifies digits after the decimal point, while for strings it truncates to the specified character count.
fmt::print("{:.2f}\n", 3.14159); // "3.14"
fmt::print("{:.4}\n", "hello world"); // "hell"
Locale-Aware Formatting
The L flag enables locale-specific formatting, inserting appropriate digit grouping separators for the current global locale.
auto loc = std::locale("en_US.UTF-8");
fmt::print("{:L}\n", 1234567890); // "1,234,567,890"
This functionality is implemented in the formatting engine within include/fmt/format-inl.h or src/format.cc, depending on your build configuration.
Compile-Time Safety with fmt::format
When using C++20 or providing compile-time constant format strings, fmt::format validates replacement fields during compilation. Invalid specifiers or type mismatches generate compilation errors rather than runtime exceptions.
// This fails to compile because 'd' is not valid for a string.
std::string s = fmt::format("{:d}", "text");
Even when format strings are determined at runtime, the library throws fmt::format_error for mismatched arguments, maintaining exception safety throughout the formatting pipeline implemented in include/fmt/core.h.
Summary
- Include
<fmt/core.h>to accessfmt::format,fmt::print, andfmt::argin the fmtlib/fmt library. - Use replacement fields
{}for automatic argument indexing, or specify positions explicitly with{0},{1}, etc. - Employ named arguments via
fmt::arg("name", value)for readability in complex formatting operations. - Control presentation using format specifiers for width (
10), alignment (<,>,^), fill characters (*), and numeric flags (+,#,0). - Leverage compile-time checking to catch format string errors at build time when using constant format strings.
- Reference implementation details in
include/fmt/core.h,include/fmt/format.h, andsrc/format.ccfor advanced customization.
Frequently Asked Questions
What header file do I need to include for fmt::format?
Include <fmt/core.h> for the basic formatting API including fmt::format and fmt::print. For advanced features or header-only usage, you may also need <fmt/format.h> or <fmt/format-inl.h> when defining FMT_HEADER_ONLY before your includes.
Can I mix positional and named arguments in fmt::format?
No, mixing automatic indexing (empty {}), explicit positional indices ({0}, {1}), and named arguments in the same format string results in a compile-time error. The parser in include/fmt/format.h enforces consistent argument referencing throughout the format string.
How does fmt::format handle compile-time format string checking?
When the format string is a compile-time constant (C++20 consteval contexts or constexpr strings), the parser runs during compilation, validating specifier compatibility with argument types. Invalid combinations like {:d} for strings cause compilation failures rather than runtime exceptions.
What is the difference between fmt::format and fmt::print?
fmt::format returns a std::string containing the formatted result, while fmt::print writes directly to stdout or a specified file stream without creating an intermediate string object. Both functions share the same replacement field syntax and parsing logic defined in include/fmt/core.h.
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 →