How to Use `fmt::print` for Console Output in C++: A Complete Guide

fmt::print is a type-safe, high-performance formatting function that writes formatted text to stdout or any FILE* stream, implemented in include/fmt/core.h as a thin wrapper around the lower-level fmt::vprint routine.

The {fmt} library (fmtlib/fmt) provides modern C++ formatting that eliminates the type-safety issues of printf and the verbosity of iostreams. fmt::print serves as the primary high-level API for console output, residing in include/fmt/core.h around line 2906. This article examines the function's overloads, implementation details, and practical usage patterns derived directly from the source code.

Basic Console Output to stdout

By default, fmt::print writes formatted output to standard output (stdout). When called without a stream argument, the function forwards to fmt::vprint(stdout, ...) internally, ensuring consistent behavior across platforms.

#include <fmt/core.h>

int main() {
    fmt::print("Hello, {}!\n", "world");          // → Hello, world!
    fmt::print("Value: {}\n", 42);
}

This overload provides the simplest path for quick debugging and user-facing messages while maintaining compile-time type safety.

Writing to Custom Streams (stderr and FILE*)

For error reporting or logging to specific file descriptors, fmt::print accepts a FILE* as its first argument. This allows direct output to stderr, log files, or any valid C file stream.

#include <fmt/core.h>
#include <cstdio>

int main() {
    fmt::print(stderr, "Error: {}\n", "something went wrong");
}

According to the fmtlib/fmt source, both the single-argument (stdout) and two-argument (FILE*) overloads resolve to the same underlying vprint implementation, guaranteeing identical formatting behavior regardless of the destination stream.

Format String Safety and Flexibility

Compile-Time Type Checking

The library validates format specifiers against argument types at compile time when using string literals or the FMT_STRING macro. This prevents runtime crashes from type mismatches that plague traditional printf calls.

The template machinery examines the format string during compilation, ensuring that {} placeholders match the provided arguments in type and count.

Runtime Format Strings

When the format string is not known at compile time (e.g., user input or configuration files), wrap the string with fmt::runtime to bypass compile-time checks while maintaining safety:

#include <fmt/core.h>
#include <string>

int main() {
    std::string fmt_str = "{:04x}";
    fmt::print(fmt::runtime(fmt_str), 255);       // → 00ff
}

This distinction allows the library to optimize compile-time cases while supporting dynamic formatting requirements.

Advanced Formatting Capabilities

Colored Terminal Output

For styled console output, include fmt/color.h and pass color specifications as the first argument. The library applies ANSI escape codes automatically:

#include <fmt/core.h>
#include <fmt/color.h>

int main() {
    fmt::print(fmt::emphasis::bold | fmt::fg(fmt::color::crimson),
               "Critical: {}\n", "failure");   // bold, red text
}

Printing Containers and Ranges

To print C++ containers directly, include fmt/ranges.h. This header provides formatters for standard containers, displaying them as human-readable lists:

#include <fmt/ranges.h>
#include <vector>

int main() {
    std::vector<int> v = {1, 2, 3};
    fmt::print("{}\n", v);                       // → [1, 2, 3]
}

For integration with C++ iostreams rather than C FILE* streams, include/fmt/ostream.h provides overloads accepting std::ostream references.

How fmt::print Works Under the Hood

The implementation in include/fmt/core.h follows a three-stage pipeline:

  1. Argument Packing – Variadic template arguments are wrapped into an fmt::format_args pack
  2. Formatting – fmt::vprint calls the core formatter (fmt::detail::vformat) which parses the format string, selects appropriate formatters for each type, and builds the output buffer
  3. Output – The final text is emitted via standard C library functions (fwrite/fputs) on the supplied FILE*

This architecture separates the type-unsafe C-style output operations from the type-safe C++ interface, minimizing template bloat while maximizing performance.

Summary

  • fmt::print is defined in include/fmt/core.h (around line 2906) and serves as the primary entry point for formatted console output
  • The function defaults to stdout but accepts any FILE* for custom stream redirection
  • Compile-time safety is enforced for literal strings; use fmt::runtime for dynamic format strings
  • Terminal styling requires include/fmt/color.h; container printing requires include/fmt/ranges.h
  • The implementation wraps fmt::vprint with fmt::format_args packing and fmt::detail::vformat processing

Frequently Asked Questions

What is the difference between fmt::print and fmt::format?

fmt::format returns a std::string containing the formatted result, while fmt::print writes directly to stdout or a specified FILE* stream without creating intermediate string objects. Use fmt::format when you need to store or manipulate the result; use fmt::print for immediate console output.

How do I print to stderr using fmt::print?

Pass stderr as the first argument before the format string: fmt::print(stderr, "Error code: {}\n", code). This overload accepts any valid FILE* pointer, allowing output to log files or other streams.

Can I use fmt::print with runtime format strings?

Yes, wrap the runtime string with fmt::runtime(): fmt::print(fmt::runtime(user_input), args...). This disables compile-time checking for that specific format string while maintaining type safety for the arguments themselves.

Is fmt::print type-safe at compile time?

Yes, when using string literals or the FMT_STRING macro, the library validates that format specifiers match the argument types at compile time, preventing mismatches that would cause undefined behavior in printf-style functions.

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 →