Previous | Table of Contents | Next |
Cs printf family of functions is an effective and often convenient I/O mechanism. It is not, however, type safe or extensible to user-defined types (classes). Consequently, I started looking for a type-safe, terse, extensible, and efficient alternative to the printf family. Part of the inspiration came from the last page and a half of the Ada rationale (Ichbiah, 1979), which is an argument that you cannot have a terse and type-safe I/O library without special language features to support it. I took that as a challenge. The result was the stream I/O library that was first implemented in 1984 and presented in Strings and Lists for C++ (Stroustrup, 1985). Soon after, Dave Presotto reimplemented the stream library without changing the interfaces.
To introduce stream I/O, this example was considered:
fprintf(stderr,x = %s\n,x);
Because fprintf() relies on unchecked arguments that are handled according to the format string at runtime, this is not type safe. Had x been a user-defined type like complex, there would have been no way of specifying the output format of x in the convenient way used for types known to printf() (for example, %s and %d). The programmer would typically have defined a separate function for printing complex numbers and then written something like this:
fprintf(stderr,x = ); put_complex(stderr,x); fprintf(stderr,\n);
This is inelegant. It would have been a major annoyance in C++ programs that use many user-defined types to represent entities that are interesting and critical to an application.
Type security and uniform treatment can be achieved by using a single overloaded function name for a set of output functions:
put(stderr,x = ); put(stderr,x); put(stderr,\n);
The type of the argument determines which put function is invoked for each argument. However, this is too verbose. The C++ solution, using an output stream for which << has been defined as a put to operator, looks like this:
cerr << x = << x << \n;
where cerr is the standard error output stream (equivalent to the C stderr). If x is an int with the value 123, this statement prints
x = 123
followed by a newline onto the standard error output stream.
This style can be used as long as x is a type for which operator << is defined, and a user can trivially define operator << for a new type. If x is the user-defined type complex with the value (1,2.4), the statement prints the following on cerr:
x = (1,2.4)
The stream I/O facility is implemented exclusively using language features available to every C++ programmer. Like C, C++ does not have any I/O facilities built into the language. The stream I/O facility is provided in a library and contains no extra-linguistic magic.
The idea of providing an output operator rather than a named output function was suggested by Doug McIlroy. This requires operators to return their left-hand operand for use by further operations.
In connection with Release 2.0, Jerry Schwarz reimplemented and partially redesigned the streams library to serve a larger class of applications and to be more efficient for file I/O. A significant improvement was the use of Andrew Koenigs idea of manipulators (Stroustrup, 1991) to control formatting details such as the precision used for floating-point output. Experience with streams was a major reason for the change to the basic type system and to the overloading rules to allow char values to be treated as characters rather than small integers the way they are in C:
char ch = b; cout << a << ch;
In Release 1.*, this code would output a string of digits reflecting the integer values of the characters a and b; whereas Release 2.* outputs ab as one would expect.
The iostreams library was further revised during the standards process. The most visible change was that the streams are now parameterized on the type used to communicate with lower-level I/O systems. This change was made to ensure that languages that require multibyte encodings, notably Japanese and Chinese, would be handled on an equal footing with single-byte characters. Similarly, the standard string type is parameterized on its character type.
Previous | Table of Contents | Next |