# How to Redirect std::ostream Output Through fmtlib: 4 Methods Explained

> Redirect std::ostream output using fmtlib with four methods. Learn how fmt::print routes formatted data to any std::basic_ostream object for efficient output management.

- Repository: [Hello World Foundation/fmt](https://github.com/fmtlib/fmt)
- Tags: how-to-guide
- Published: 2026-09-11

---

**You can redirect `std::ostream` output through fmtlib by using the `fmt::print` overload that accepts a stream reference, which routes formatted data through `vprint` and `write_buffer` to any standard or custom `std::basic_ostream` object.**

The {fmt} library provides a high-performance bridge between its modern formatting engine and the C++ standard library's stream hierarchy. Whether you need to write to `std::cout`, capture output in a `std::ostringstream`, or target a custom stream buffer, fmtlib integrates seamlessly without sacrificing type safety or performance. This guide examines the implementation details found in the [fmtlib/fmt](https://github.com/fmtlib/fmt) repository to show exactly how redirection works.

## The Three Components of the Ostream Bridge

The integration lives primarily in [`include/fmt/ostream.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/ostream.h) and relies on three key mechanisms working in concert.

### `basic_ostream_formatter` for Streamable Objects

Located at [`include/fmt/ostream.h#L76`](https://github.com/fmtlib/fmt/blob/main/include/fmt/ostream.h#L76), the `basic_ostream_formatter` struct enables formatting of any type that implements `operator<<`. It constructs a temporary buffer, streams the value into a `std::basic_ostream`, and returns the result as a `basic_string_view`.

### `vprint` for Low-Level Stream Writing

The `vprint` function at [`include/fmt/ostream.h#L18`](https://github.com/fmtlib/fmt/blob/main/include/fmt/ostream.h#L18) handles the actual byte transfer. It accepts a pre-compiled `format_args` object and writes the result to the supplied `std::ostream` using `detail::write_buffer`. On Windows, it detects console streams via `_isatty` and may bypass the C++ stream layer entirely for improved performance through `detail::write_console`.

### `print` and `println` as User-Facing APIs

The high-level entry points at [`include/fmt/ostream.h#L53`](https://github.com/fmtlib/fmt/blob/main/include/fmt/ostream.h#L53) build a `basic_memory_buffer`, populate it using `detail::vformat_to`, then delegate to `detail::write_buffer` to flush content to the stream.

## Redirecting Output to Standard Streams

To redirect formatted output through any `std::ostream`, pass the stream as the first argument to `fmt::print`.

### Writing to std::cout

```cpp
#include <fmt/ostream.h>

int main() {
    fmt::print(std::cout, "Hello, {}!\n", "world");
}

```

This follows the path: `print` → `vprint` → `detail::write_buffer` (lines 53–59).

### Capturing Output in std::ostringstream

```cpp
#include <sstream>
#include <fmt/ostream.h>

int main() {
    std::ostringstream ss;
    fmt::print(ss, "The answer is {}.", 42);
    // ss now contains: "The answer is 42."
}

```

The same implementation writes into the string stream’s internal buffer via `write_buffer`, handling `streamsize` limits safely.

## Formatting Stream-Only Types with fmt::streamed

When working with types that lack a native fmt formatter but provide `operator<<` (such as `std::chrono::duration`), use `fmt::streamed`.

```cpp
#include <fmt/ostream.h>
#include <chrono>

int main() {
    fmt::print(std::cout,
               "Elapsed: {}\n",
               fmt::streamed(std::chrono::seconds(5)));
}

```

`fmt::streamed` creates a `detail::streamed_view<T>` (lines 68–73) that `basic_ostream_formatter` (lines 76–90) renders by streaming the value into a temporary `std::basic_ostream` before extracting the formatted result.

## Direct std::ostream Redirection with vprint

For performance-critical code paths where you already possess a `format_args` object, use the low-level `vprint` API directly.

```cpp
#include <fmt/ostream.h>
#include <fmt/format.h>

int main() {
    auto fmt_str = fmt::format_string<int, const char*>("{} {}", "{}");
    fmt::format_args args = fmt::make_format_args(123, "abc");
    fmt::vprint(std::cerr, fmt_str, args);
}

```

This bypasses template expansion overhead and writes immediately to `std::cerr` using the same `write_buffer` infrastructure.

## Summary

- **`fmt::print(os, ...)`** redirects formatted output to any `std::basic_ostream` by writing to a `basic_memory_buffer` then flushing via `detail::write_buffer`.
- **Three core components** in [`include/fmt/ostream.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/ostream.h) manage this bridge: `basic_ostream_formatter` (line 76), `vprint` (line 18), and the `print` overloads (line 53).
- **`fmt::streamed`** enables modern fmt syntax for legacy streamable types through `detail::streamed_view`.
- **`vprint`** provides direct access for pre-compiled format arguments, supporting both standard streams and Windows console optimizations.

## Frequently Asked Questions

### Can I redirect fmtlib output to a custom std::ostream class?

Yes. Because the implementation operates on `std::basic_ostream<Char>`, any class inheriting from this base—including custom stream buffers—works with `fmt::print` and `fmt::vprint`. The library writes via `detail::write_buffer`, which respects the stream's `streamsize` limits and exception masks.

### How does fmt::streamed interact with the ostream formatter?

`fmt::streamed(value)` constructs a `detail::streamed_view<T>` that holds a reference to your object. When formatting occurs, `basic_ostream_formatter` (lines 76–90) streams this view into a temporary `std::basic_ostream`, captures the resulting characters, and appends them to the output buffer without modifying your original stream object.

### Is fmt::print slower than direct std::ostream insertion?

No. According to the source code in [`include/fmt/ostream.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/ostream.h), `fmt::print` minimizes overhead by building a contiguous `basic_memory_buffer` before calling `write_buffer` once. On Windows, it even bypasses the C++ stream layer for console outputs via `_isatty` checks, often outperforming repeated `operator<<` calls.

### What files are required to use ostream redirection?

You need [`include/fmt/ostream.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/ostream.h) for the `print`, `vprint`, and `streamed` APIs. This header depends on [`include/fmt/core.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/core.h) for `basic_memory_buffer` and `format_args`, and [`include/fmt/format.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format.h) if you use `format_string` or `make_format_args` for type-safe argument construction.