Custom C++ Stream Manipulators
Tags: cpp
Published: 2024-04-09 14:07:09

---
title: Custom C++ Stream Manipulators
published: true
description: Implementing custom stream manipulators in C++.
tags: #cpp
# cover_image: https://direct_url_to_image.jpg
# Use a ratio of 100:42 for best results.
# published_at: 2024-04-02 13:27 +0000
---

## Introduction

To print a user-defined type in C, you typically implement a function to do it, for example:

```c
struct point {
  int x, y;
};

void print_point( struct point const *p, FILE *f ) {
  fprintf( f, "(%d,%d)", p->x, p->y );
}

void f( struct point const *p ) {
  printf( "Origin: " );
  print_point( p, stdout );
  putchar( '\n' );
}
```

One of the nice things about the [C++ I/O library](https://en.cppreference.com/w/cpp/io) is its overloading of `<<` as a “stream insertion operator” to be [syntactic sugar](https://en.wikipedia.org/wiki/Syntactic_sugar) for chaining the printing of a sequence of items including user-defined types:

```cpp
std::cout << "Origin: " << p << '\n';
```

To make `<<` work for a user-defined type, you simply overload `<<` for it.

## Overloading `<<` for a User-Defined Type

To make `<<` work for `point`:

```cpp
std::ostream& operator<<( std::ostream &o, point const &p ) {
  return o << '(' << p.x << ',' << p.y << ')';
}
```

The recipe for a user-defined type is:
 
+ The first parameter is `std::ostream&`.
+ The second parameter is a `const&` to the user-defined type (which must be a `struct`, `union`, `class`, or `enum`).
+ Return the stream argument.

As a general rule, you should _not_ print a newline — let the caller do it.

## Stream Manipulators

Things like [`std::endl`](https://en.cppreference.com/w/cpp/io/manip/endl) are stream _manipulators_, that is they “manipulate” a stream in some way, but don’t involve any other object.

To implement your own manipulator, say to begin printing in a particular color on a terminal, you can do:

```cpp
inline std::ostream& red( std::ostream &o ) {
  return o << "\33[31m";
}

inline std::ostream& endcolor( std::ostream &o ) {
  return o << "\33[m";
}

void f() {
  // ...
  std::cout << red << "error" << endcolor << ": oops\n";
```

The recipe for a manipulator is:

+ The only parameter is `std::ostream&`.
+ Return the stream argument.

> Setting colors on ANSI terminals is done via [SGR (Select Graphic Rendition) parameters](https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters).

## Stream Manipulators with Parameters

If you want to have a manipulator like [`std::setw()`](https://en.cppreference.com/w/cpp/io/manip/setw) that takes a parameter, you need a helper object. For example, to implement a manipulator to indent (print) a given number of spaces:

```cpp
class indent {
public:
  explicit constexpr indent( unsigned n ) : _indent{ n } { }
private:
  unsigned const _indent;

  friend std::ostream& operator<<( std::ostream &o,
                                   indent const &i ) {
    return o << std::setw( i._indent ) << "";
  }
};

void f() {
  // ...
  std::cout << indent(4) << "hello, world!\n";
```

This works because:

1. The `indent(4)` is a constructor call rather than a function call that creates a temporary object to remember the indentation amount.
2. The overloaded `<<` will then “print” the object by using `setw()` to set the field width to 4 then printing the empty string will print 4 spaces (the default stream fill character) before it.

The recipe for a manipulator with parameters is:

+ Create a class having the name of the manipulator with `constexpr` constructor(s) that take the parameter(s) you want storing the value(s) as `const` data members.
+ Define a `friend` overloaded operator `<<` that prints the user-defined type of the manipulator class.

Note that the compiler will very likely [optimize away](https://godbolt.org/z/7c8rb1vqs) the temporary object.

## Maintaining State

Suppose you want to expand upon `indent` and have the stream remember what its current indentation is as well as either increment or decrement it, for example:

```cpp
std::cout << "name : {\n"
          << inc_indent
          << indent << "last: \"" << last << "\"\n"
          << indent << "first: \"" << first << "\"\n"
          << dec_indent
          << "}\n";
```

It turns out that streams have the feature whereby you can associate arbitrary data with them:

+ Every stream object internally maintains an array of `long` for user-defined data.  (It also maintains an additional array of `void*`, but that’s a story for another time.)

+ The [`xalloc()`](https://en.cppreference.com/w/cpp/io/ios_base/xalloc) function gives you an index for your exclusive use into that array.

+ The [`iword()`](https://en.cppreference.com/w/cpp/io/ios_base/iword) function returns a reference to the `long` at that index that you can use to store whatever you want — in this case, the current indentation.

Given that, we can then implement:

```cpp
long& indent_of( std::ios_base &b ) {
  static int const index = ios_base::xalloc();
  return b.iword( index );
}
```

that gets a reference to the current indentation.  Some notes:

+ The class [`ios_base`](https://en.cppreference.com/w/cpp/io/ios_base) is used because it’s the base class for `ostream`.  We use it rather than `ostream` because `ios_base` is all we need here.

+ Calling `xalloc()` is guaranteed to be thread-safe, i.e., the index it returns is guaranteed to be unique.

+ The `long` to which `iword(index)` refers is guaranteed to be initialized to `0`.


Given `indent_of()`, we can now implement:


```cpp
inline std::ostream& indent( std::ostream &o ) {
  o << std::setw( static_cast<int>( indent_of( o ) ) * 4 ) << "";
  return o;
}

inline std::ostream& inc_indent( std::ostream &o ) {
  ++indent_of( o );
  return o;
}

inline std::ostream& dec_indent( std::ostream &o ) {
  auto &o_indent = indent_of( o );
  if ( o_indent > 0 )  // ensure indent stays non-negative
    --o_indent;
  return o;
}
```

> In the original example, the “indent” was the number of spaces to indent.  For this example, the “indent” is now the number of “indentation levels” and each level is arbitrarily scaled by 4 spaces.  Alternatively, there could be a global `indent_scale` variable that you can set to alter the scaling.  You can even have a scale per stream, but that’s more complicated and therefore a story for another time.

## Conclusion

Unlike the C I/O library, the C++ I/O library is extensible for both user-defined types via operator overloading and storing user-defined data via `xalloc()` and `iword()`.