iostreams: Why so little information? Wednesday 20th June 2007 3 Comments

Most people’s exposure to C++ streams is with a “Hello, world” style program.

std::cout << "Hello, world!\n";

From there you move on to how you can “<<” different types and how you can produce the equivalent of something like the code below with only about 3 times as many characters.

printf("%08x : %.17g", uParm1, dParm2);

It’s not exactly a winning argument for using iostreams over printf and its friends.

So where is the power of iostreams? Well, stage two comes with the introduction of the ability to define your own insertion and extraction operations.

std::ostream& operator<<(std::ostream& , const MyClass&);
std::istream& operator>>(std::istream& , MyClass&);

The syntax isn’t trivial for beginners and, due to the way that the operators take a standard class on the left, you probably have to introduce the concept of friendship, but it’s pretty trivial.

So this is all well and good. We’ve created our own types and because we have defined the insertion and extraction operators in terms of the istream and ostream classes we can take advantage of writing to string, file, anything in fact!

Excellent, lets write an ostream that writes to a MyBucket class. By this I mean that I have a MyBucket class that can consume bytes and arranges them prettily in a plastic bucket. I’d like to wrap an ostream interface around MyBucket so that objects that have an operator<< onto and ostream can write into MyBucket. Should be easy, right?

So how do we do that?

This is where I found there to be a lack of readily available documentation how extend the iostream library in this way.

Naïvely, you might think that we have an inteface class, ostream, and we want to change its functionality so perhaps we should derive a class from it and override the functions we want to change. If you have a look at the documentation for ostream you’ll discover that there are virtually no virtual functions. It does have a virtual destructor. This means that if you pass it into a function like std::ostream& operator<<(std::ostream&, const SomeoneElsesClass&); then you’re not going to be able to influence anything that happens inside this class from your derived ostream.

The “correct” answer is that you need to write a specialized std::streambuf. The streambuf interface abstracts the raw byte buffer functionality of files, strings, etc. from the [io]stream classes and allows the [io]stream class to perform all the formatting functionality that is what they are there for.

Once you have a class MyBucketStreamBuf : public std::streambuf { ... you can then construct [io]streams around it an pass them around as any other [io]stream class. ostream (really std::basic_ostream) has an explicit constructor that takes a pointer to (any form of) streambuf, or if you want to you can now derive from ostream and have it manage the lifetime of the custom streambuf class, and initialize the base ostream class with a pointer to it. Easy!

So why is there so little documentation on how to do anything more with iostreams than define your own insertion and extraction operations?

Entry number one Tuesday 19th June 2007 Comments Off on Entry number one

Well this is it, it looks like I’ve managed to install WordPress in my webspace. Now I just need something to write about…