# How fmtlib Implements the Ostream Formatter Extension for operator<<

> Discover how fmtlib implements its ostream formatter extension for operator<<. Learn about basic_ostream_formatter, streamed_view, and fmt::streamed() for seamless type formatting.

- Repository: [Hello World Foundation/fmt](https://github.com/fmtlib/fmt)
- Tags: internals
- Published: 2026-09-05

---

**The fmt library enables formatting of any type supporting `operator<<` through a three-part mechanism in [`include/fmt/ostream.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/ostream.h): `basic_ostream_formatter` streams values into a temporary buffer, `streamed_view` acts as a type wrapper, and `fmt::streamed()` provides a user-facing API.**

The `{fmt}` library offers flexible C++ formatting that extends beyond built-in types. When you need to format custom classes that already implement `std::ostream` insertion operators, fmtlib provides a specialized **ostream formatter extension** that bridges standard stream operations with the library's high-performance formatting pipeline.

## Overview of the Ostream Formatter Extension

Unlike fmtlib's core formatters that write directly to output iterators, the ostream extension accommodates legacy codebases and third-party types that only expose `operator<<`. This extension lives entirely within **[`include/fmt/ostream.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/ostream.h)** and leverages `std::basic_ostream` internally while presenting a modern `fmt::format` interface to users.

## The Three-Part Implementation

The implementation consists of three coordinated components that transform streamed output into fmtlib's native buffer-based formatting system.

### basic_ostream_formatter: The Core Engine

At the heart of the extension lies `basic_ostream_formatter`, a class template that constructs a temporary string by streaming values into a `std::basic_ostream`. According to the fmtlib source code in [`include/fmt/ostream.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/ostream.h), the formatter uses a `basic_memory_buffer` to capture output before transferring it to fmtlib's formatting pipeline:

```cpp
template <typename Char>
struct basic_ostream_formatter : formatter<basic_string_view<Char>, Char> {
  template <typename T, typename Context>
  auto format(const T& value, Context& ctx) const -> decltype(ctx.out()) {
    auto buffer = basic_memory_buffer<Char>();
    auto&& formatbuf = detail::formatbuf<std::basic_streambuf<Char>>(buffer);
    auto&& output = std::basic_ostream<Char>(&formatbuf);
    output.imbue(std::.locale::classic());
    output << value;                         // ← uses the user‑provided operator<<
    output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
    return formatter<basic_string_view<Char>, Char>::format(
        {buffer.data(), buffer.size()}, ctx);
  }
};

```

**Key technical details:**
- **`basic_memory_buffer<Char>`**: Allocates a stack-backed buffer to minimize heap allocations during streaming.
- **`detail::formatbuf`**: Wraps the memory buffer in a `std::basic_streambuf` interface, allowing `std::basic_ostream` to write directly into fmtlib's memory management system.
- **Locale handling**: The formatter explicitly imbues the classic "C" locale to ensure consistent behavior across platforms.
- **Exception masking**: Sets exception flags to catch stream errors before they propagate into the formatting context.

### streamed_view: The Type Wrapper

To distinguish between types that should use ostream formatting versus native fmtlib formatters, the library introduces `detail::streamed_view<T>`. The specialization of `formatter` for this wrapper connects the generic view to `basic_ostream_formatter`:

```cpp
template <typename T, typename Char>
struct formatter<detail::streamed_view<T>, Char>
    : basic_ostream_formatter<Char> {
  template <typename Context>
  auto format(detail::streamed_view<T> view, Context& ctx) const
      -> decltype(ctx.out()) {
    return basic_ostream_formatter<Char>::format(view.value, ctx);
  }
};

```

This indirection allows fmtlib to treat ostream-formatted types as first-class citizens within the format string parsing system while maintaining compile-time type safety.

### fmt::streamed: The User Interface

Users interact with this system through the **`fmt::streamed`** helper function, defined at lines 113-116 of [`include/fmt/ostream.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/ostream.h). This constexpr function creates the `streamed_view` wrapper without exposing implementation details:

```cpp
template <typename T>
constexpr auto streamed(const T& value) -> detail::streamed_view<T> {
  return {value};
}

```

## Practical Usage Example

To format a custom type using its `operator<<`, include [`fmt/ostream.h`](https://github.com/fmtlib/fmt/blob/main/fmt/ostream.h) and wrap your value with `fmt::streamed()`:

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

struct Point {
  int x, y;
};

std::ostream& operator<<(std::ostream& os, const Point& p) {
  return os << '(' << p.x << ", " << p.y << ')';
}

int main() {
  Point pt{3, 7};
  
  // Use fmt::streamed to invoke operator<< based formatting
  std::string s = fmt::format("The point is {}", fmt::streamed(pt));
  // Result: "The point is (3, 7)"
  
  // Works with fmt::print to any ostream
  fmt::print(std::cout, "Point: {}\n", fmt::streamed(pt));
  // Prints: Point: (3, 7)
}

```

**What happens at runtime:**
1. `fmt::streamed(pt)` constructs a `detail::streamed_view<Point>` containing a reference to `pt`.
2. The format string parser selects `formatter<detail::streamed_view<Point>, char>` due to the wrapped type.
3. The specialization delegates to `basic_ostream_formatter<char>::format()`, which creates a temporary `std::ostream` backed by fmtlib's memory buffer.
4. `operator<<` streams into this buffer, and the resulting string view feeds back into the standard formatting pipeline.

## Summary

- **`basic_ostream_formatter`** in [`include/fmt/ostream.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/ostream.h) provides the machinery to capture `operator<<` output into fmtlib memory buffers using `detail::formatbuf`.
- **`detail::streamed_view<T>`** acts as a type tag that triggers ostream-based formatting through template specialization.
- **`fmt::streamed()`** offers a clean, constexpr API for opting into ostream formatting within `fmt::format` calls.
- The extension seamlessly integrates legacy stream-based types with modern fmtlib formatting without requiring custom formatter specializations.

## Frequently Asked Questions

### How do I format a type that only has operator<< with fmtlib?

Wrap your value with `fmt::streamed()` inside the format string. Ensure you include [`fmt/ostream.h`](https://github.com/fmtlib/fmt/blob/main/fmt/ostream.h) to access the ostream formatter extension. This tells fmtlib to use the standard stream insertion operator rather than looking for a native `formatter` specialization.

### Why does fmtlib use a memory buffer instead of stringstream?

The implementation uses `basic_memory_buffer` instead of `std::stringstream` to avoid the overhead of `std::string` allocations and to maintain compatibility with fmtlib's output iterator architecture. The `detail::formatbuf` adapter allows `std::basic_ostream` to write directly into fmtlib's optimized buffer pool, resulting in better performance than standard string streams.

### Can I customize the locale when using the ostream formatter?

The `basic_ostream_formatter` explicitly imbues the classic "C" locale (`std::locale::classic()`) on the temporary stream object. If you need locale-specific formatting, you must implement a custom `formatter` specialization rather than relying on the ostream extension, as the current implementation forces classic locale behavior to ensure consistency across platforms.

### Is there a performance penalty for using fmt::streamed versus a native formatter?

Yes, the ostream formatter extension incurs overhead compared to native fmtlib formatters. It requires constructing a `std::basic_ostream` object, allocating a `basic_memory_buffer`, and virtual function calls through the stream buffer interface. For hot paths, implementing a direct `formatter<T>` specialization that writes to `ctx.out()` avoids this indirection and provides significantly better performance.