Who architected / designed C++'s IOStreams, and would it still be considered well-designed by today's standards?

Regarding who designed them, the original library was (not surprisingly) created by Bjarne Stroustrup, and then reimplemented by Dave Presotto. This was then redesigned and reimplemented yet again by Jerry Schwarz for Cfront 2.0, using the idea of manipulators from Andrew Koenig. The standard version of the library is based on this implementation.

Source "The Design & Evolution of C++", section 8.3.1.


Several ill-conceived ideas found their way into the standard: auto_ptr, vector<bool>, valarray and export, just to name a few. So I wouldn't take the presence of IOStreams necessarily as a sign of quality design.

IOStreams have a checkered history. They are actually a reworking of an earlier streams library, but were authored at a time when many of today's C++ idioms didn't exist, so the designers didn't have the benefit of hindsight. One issue that only became apparent over time was that it is almost impossible to implement IOStreams as efficiently as C's stdio, due to the copious use of virtual functions and forwarding to internal buffer objects at even the finest granularity, and also thanks to some inscrutable strangeness in the way locales are defined and implemented. My memory of this is quite fuzzy, I'll admit; I remember it being the subject of intense debate some years ago, on comp.lang.c++.moderated.


If you had to judge by today's software engineering standards (if there actually is any general agreement on these), would C++'s IOStreams still be considered well-designed? (I wouldn't want to improve my software design skills from something that's generally considered outdated.)

I would say NO, for several reasons:

Poor error handling

Error conditions should be reported with exceptions, not with operator void*.

The "zombie object" anti-pattern is what causes bugs like these.

Poor separation between formatting and I/O

This makes stream objects unnecessary complex, as they have to contain extra state information for formatting, whether you need it or not.

It also increases the odds of writing bugs like:

using namespace std; // I'm lazy.
cout << hex << setw(8) << setfill('0') << x << endl;
// Oops!  Forgot to set the stream back to decimal mode.

If instead, you wrote something like:

cout << pad(to_hex(x), 8, '0') << endl;

There would be no formatting-related state bits, and no problem.

Note that in "modern" languages like Java, C#, and Python, all objects have a toString/ToString/__str__ function that is called by the I/O routines. AFAIK, only C++ does it the other way around by using stringstream as the standard way of converting to a string.

Poor support for i18n

Iostream-based output splits string literals into pieces.

cout << "My name is " << name << " and I am " << occupation << " from " << hometown << endl;

Format strings put whole sentences into string literals.

printf("My name is %s and I am %s from %s.\n", name, occupation, hometown);

The latter approach is easier to adapt to internationalization libraries like GNU gettext, because the use of whole sentences provides more context for the translators. If your string formatting routine supports re-ordering (like the POSIX $ printf parameters), then it also better handles differences in word order between languages.

Tags:

C++

Iostream