Writing your own stream manipulators

Kevlin Henney


Still glides the Stream, and shall for ever glide;
The Form remains, the Function never dies.

William Wordsworth


Streams are typically more than just dumping or loading grounds for basic data. Their strength lies in customisation and the accommodation of new data types. As objects, their state may also be controlled via either a member function or a manipulator interface; I am going to concentrate on manipulators. I'll wrap up the article with a worked example.


Simple manipulators

Data flow and control are the two basic operations on a stream. Returning references and the syntactic sugar of operator overloading offer a convenient way to chain together successive items for input or output in a single expression: The last item in the expression above provides more control than data: a newline is inserted and the buffered output is flushed. Using the endl stream manipulator is a more common idiom than using a naked '\n' in C++. The equivalent expression without manipulators is slightly less elegant: Other simple manipulators include: ws, to act as a sink and eat whitespace from an istream; ends, to null terminate a string; flush, to flush an ostream; and hex, dec and oct, to convert input from and output to the appropriate base. These are all simply function names. In addition to the insertion and extraction member operators for basic types, the istream and ostream classes both have operators that take a function pointer to execute on the current stream.

Given this, it is possible to define your own manipulators to control layout and stream state:

These manipulators may be used both indirectly and directly: The indirect form offers the syntactic convenience of mixing control and data into a sequence of insertions or extractions from a stream.


Parameterised manipulators

All the manipulators shown above are simple functions. They perform a single task well but without any flexibility. A standard IOStream implementation also provides a number of manipulators taking the form of function calls to control the stream. For instance to set the output precision for floating point numbers: Using this approach you can implement more general control over the stream: There are a number of different methods for implementing these manipulators. The most obvious method is to implement these manipulators as functions that return objects for which insertion or extraction operators are defined, e.g.: Such classes are often not intended for general use and have mangled names or friend-only access. A more direct and, in my opinion, more general solution is to cut out the function and use objects directly. A constructor call to create a temporary object looks like a function call with much the same effect: The class-based approach offers a more obvious route to creating custom manipulator objects: Objects can act as pseudo-functions or functors. This is a powerful idiom deriving from use of the function call operator, so that direct application of manipulators on streams becomes possible:


An example

Consider a manipulator that takes an integer and expands it out into word form, e.g.: will print out The class holds a single value and is not designed as a base class, so the default copy constructor, assignment operator and destructor are usable — they may be inserted for completeness, but for the sake of brevity I will omit them from this article: The operator() member function does the hard work, using the literal constant tables tens ("ten" to "ninety", starting from the first element rather than the zeroth) and units_and_teens ("zero" to "nineteen"). This makes the stream insertion operator quite trivial: Notice also that friendship of the class is not required for the insertion operator; the property of expression on an ostream is a feature of the class and may be used separately. The algorithm for writing out the number is mutually recursive. You are welcome to try a non-recursive version if you wish, but you will gain little except a fuller understanding of why the simplest solution is recursive! For sensible output, this implementation assumes that shorts are no more than 16-bits wide: There are a number of obvious improvements and extensions that may be made, but have not been included in this article for reasons of relevance and brevity:

© Kevlin Henney
First published in Overload 5, August/September 1994
Converted to HTML, March 2001