๐งฉ What Is the {fmt} Library? #
The {fmt} library is a modern, high-performance formatting library for C++. It provides a safe, fast, and expressive alternative to both Cโs printf family and C++โs iostreams.
It combines:
- The type safety of C++ streams
- The performance of
printf - A clean, readable syntax inspired by Python and Rust
Most importantly, {fmt} is the foundation of std::format, which was standardized in C++20.
โ๏ธ Installation and Basic Setup #
The library can be used either as a compiled dependency or as header-only, which is ideal for small tools and utilities.
Header-Only Setup #
#define FMT_HEADER_ONLY
#include "fmt/core.h"
int main() {
fmt::print("Hello, {fmt}!\n");
}
This approach:
- Requires no linker configuration
- Works well for embedded tools and utilities
- Is convenient for quick prototyping
๐งช Core Syntax: {} Placeholders
#
Unlike printf, which relies on cryptic % specifiers, {fmt} uses curly braces {} as placeholders.
Simple Output #
fmt::print("Answer = {}\n", 42);
Output:
Answer = 42
Formatting into a String #
std::string msg = fmt::format("Voltage = {} V", 3.3);
This is ideal for:
- Logging systems
- Error messages
- Network payload construction
๐ข Argument Handling and Ordering #
Each {} corresponds to an argument passed to the formatting function.
Sequential Arguments (Default) #
fmt::println("{} + {} = {}", 2, 3, 5);
Positional Arguments #
fmt::println("{1} then {0}", "first", "second");
Output:
second then first
Named Arguments #
Named arguments greatly improve readability for complex formats.
fmt::print(
"User: {name}, Score: {score}\n",
fmt::arg("name", "Alice"),
fmt::arg("score", 95)
);
๐๏ธ Format Specifiers in Detail #
Format specifiers appear after a colon : inside the braces.
fmt::print("{:format_spec}", value);
Width, Alignment, and Padding #
fmt::println("|{:>10}|", "right");
fmt::println("|{:^10}|", "center");
fmt::println("|{:<10}|", "left");
fmt::println("|{:*^10}|", "fmt");
Output:
|***fmt****|
Floating-Point Precision #
fmt::println("{:.2f}", 3.1415926);
fmt::println("{:.4f}", 3.1415926);
Integer Bases #
fmt::println("Dec: {}", 42);
fmt::println("Hex: {:x}", 42);
fmt::println("Bin: {:b}", 42);
๐งฑ Formatting Custom Types #
One of {fmt}โs most powerful features is custom formatters.
Example: Formatting a Struct #
struct Point {
int x;
int y;
};
template <>
struct fmt::formatter<Point> {
constexpr auto parse(format_parse_context& ctx) {
return ctx.begin();
}
template <typename FormatContext>
auto format(const Point& p, FormatContext& ctx) {
return fmt::format_to(ctx.out(), "({}, {})", p.x, p.y);
}
};
Usage:
Point p{3, 4};
fmt::println("Point = {}", p);
This makes {fmt} ideal for:
- Debug output
- Logging complex data structures
- Diagnostics in large systems
๐ Performance and Safety Advantages #
Why {fmt} Beats printf
#
- Compile-time format string checking
- No undefined behavior from mismatched arguments
- Easier refactoring
Why {fmt} Beats std::cout
#
- No heavy stream state
- Fewer temporary objects
- Significantly faster in hot paths
This makes {fmt} particularly attractive for:
- High-frequency logging
- Networking and protocol stacks
- Performance-sensitive tools
๐ Relationship with std::format
#
| Feature | {fmt} | std::format |
|---|---|---|
| Maturity | Very mature | Newer |
| Performance | Often faster | Slightly slower |
| Customization | Extensive | Limited |
| Standardized | No | Yes (C++20) |
Many projects still prefer {fmt} even on C++20+ compilers due to its richer feature set and battle-tested stability.
๐ Comparison Summary #
| Feature | {fmt} | printf |
std::cout |
|---|---|---|---|
| Type Safety | โ | โ | โ |
| Readability | High | Low | Medium |
| Performance | High | Medium | Low |
| Extensibility | Excellent | None | Limited |
| Modern C++ | โ | โ | โ ๏ธ |
โ Final Thoughts #
If you are writing modern C++, {fmt} should be your default formatting choice:
- Cleaner than
cout - Safer than
printf - Faster than both
- Future-proof thanks to C++20 adoption
Once you adopt {fmt}, going back to stream operators feels like stepping into the past.