How fmtlib Integrates with C++20 std::format and C++23 std::print

The fmt library provides seamless interoperability with C++20 and C++23 formatting standards through the fmt/std.h compatibility header, which exposes std::format and std::print as thin wrappers around the library's core implementation.

The {fmt} library predates the standard formatting facilities introduced in C++20 and C++23, yet it maintains full compatibility with these newer APIs. Through a carefully designed compatibility layer, fmtlib integrates with C++20 std::format and C++23 std::print while preserving a single high-performance implementation. This allows developers to adopt standard APIs immediately while benefiting from fmt's optimized formatting engine.

The Compatibility Layer: fmt/std.h

The integration centers on include/fmt/std.h, a compatibility header that bridges the gap between the fmt API and the Standard Library. When included, this header conditionally injects fmt's formatting engine into the std namespace based on available language features.

Exposing std::format (C++20)

When the feature-test macro __cpp_lib_format is satisfied, fmt/std.h brings fmt::format into the std namespace. This ensures that a call to std::format("{:02}", 7) compiles to the same code path as fmt::format("{:02}", 7), guaranteeing identical performance and behavior. The implementation relies on the core formatting engine defined in include/fmt/format.h and src/format.cc, ensuring a single source of truth for parsing and formatting logic.

Enabling std::print (C++23)

For C++23 support, the header checks for the __cpp_lib_print macro and defines std::print and std::println as thin wrappers forwarding to fmt::print and fmt::println. This makes the new "printf-like" functions available instantly without requiring a separate Standard Library implementation. The signatures match the standard exactly, ensuring that existing code using std::print works unchanged while leveraging fmt's high-performance output routines.

Implementation Architecture and Source Files

Rather than duplicating logic, fmtlib maintains one high-performance formatting core. The std namespace façade in include/fmt/std.h simply delegates to the implementation in include/fmt/format.h and src/format.cc. This design eliminates code duplication while ensuring that code using std::format and code using fmt::format share the same optimized path.

Because the overloads reside in the std namespace but rely on the fmt implementation, Argument-Dependent Lookup (ADL) remains unambiguous. The architecture allows code that prefers the original fmt:: namespace to continue compiling alongside newer code adopting the standard APIs.

Formatter Specializations for Standard Library Types

The compatibility header provides required formatter specializations for Standard Library types that lack built-in support. These include std::filesystem::path, std::optional, std::variant, std::expected, and std::source_location. These specializations ensure that complex standard types format correctly whether you use fmt::format or std::format.

The header uses conditional compilation via FMT_HAS_INCLUDE(<filesystem>) to include only the specializations relevant to the current platform. This prevents compilation failures on older toolchains while ensuring modern types are supported when available.

Conditional Compilation and ABI Safety

fmt/std.h uses rigorous feature detection to ensure broad compatibility across compiler versions. It checks FMT_CPLUSPLUS >= 202002L to determine C++20 availability and uses __cpp_lib_print to detect C++23 <print> support. The header only pulls in Standard Library facilities actually present on the target platform, maintaining ABI compatibility and preventing link-time errors.

This conditional approach means that including fmt/std.h on a C++17 compiler causes no errors, while the same header provides full std::format and std::print support on C++23 compilers.

Practical Code Examples

The following examples demonstrate how fmtlib enables both direct library usage and standard API compatibility.

Using fmt Directly

This approach works on all supported compilers regardless of C++ standard version.

#include <fmt/core.h>

int main() {
    fmt::print("Hello, {}!\n", "world");
    std::string s = fmt::format("{:04}", 42);
}

Using the Standard Façade (C++20 / C++23)

Including fmt/std.h pulls fmt into the std namespace when standard support is detected.

#include <fmt/std.h>

int main() {
    std::print("Hello, {}!\n", "world");      // C++23
    std::string s = std::format("{:04}", 42); // C++20
}

Formatting Standard Library Types

Standard types like std::filesystem::path work with both APIs thanks to the specializations in fmt/std.h.

#include <fmt/std.h>
#include <filesystem>

int main() {
    std::filesystem::path p{"/tmp/file.txt"};
    fmt::print("Path (fmt):   {}\n", p);
    std::print("Path (std):   {}\n", p);
}

Summary

  • include/fmt/std.h serves as the compatibility bridge between fmt and Standard Library formatting APIs
  • std::format (C++20) and std::print (C++23) are exposed as wrappers around fmt::format and fmt::print
  • The architecture uses a single implementation core in src/format.cc shared by both the fmt and std namespaces
  • Formatter specializations for std::filesystem::path, std::optional, std::variant, and other types are provided automatically
  • Feature-test macros __cpp_lib_format and __cpp_lib_print enable conditional support based on compiler capabilities

Frequently Asked Questions

What is the purpose of fmt/std.h?

The fmt/std.h header provides a compatibility layer that exposes fmt's formatting implementation through the standard std::format and std::print APIs. It allows developers to write code using C++20 and C++23 standard formatting functions while utilizing fmt's high-performance backend. According to the fmtlib source code, this header also provides necessary formatter specializations for standard types like std::filesystem::path and std::optional.

Does using std::format via fmtlib impact performance?

No, there is zero performance penalty. When you include fmt/std.h and call std::format, you are invoking the exact same code path as fmt::format. As implemented in include/fmt/format.h and src/format.cc, the standard namespace functions are thin wrappers that forward directly to the optimized fmt core. This single-source-of-truth architecture ensures identical parsing speed and memory allocation behavior between the two APIs.

Which C++ Standard Library types gain formatter support?

The fmt/std.h header provides formatter specializations for std::filesystem::path, std::optional, std::variant, std::expected (C++23), and std::source_location. These specializations are conditionally compiled based on the availability of headers like <filesystem> and <optional>, ensuring compatibility across different standard library implementations. This allows these complex types to be formatted directly in format strings without requiring manual conversion.

How does fmtlib handle compilers without native C++23 <print> support?

The library uses the feature-test macro __cpp_lib_print to detect whether the compiler provides a native <print> header. If not defined, fmt/std.h still provides std::print and std::println as wrappers around fmt::print, making C++23 functionality available on older compilers. This conditional compilation approach, combined with checks for FMT_CPLUSPLUS >= 202002L, ensures that the header compiles cleanly on C++17 while enabling full standard compliance on C++23 toolchains.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →