Combining User-Defined Inserters, Extractors, and Manipulators in C++
- Using Manipulators To Control Object Format
- Connecting Algorithms and Containers to Streams
- Summary
Let's take a look at how to use object streaming with the STL containers by combining inserters, extractors, and manipulators with stream iterators. We'll show how to use manipulators to control the format of objects as they're inserted into or extracted from STL containers. We'll also explore how to use custom ostream, istream, and streambuf classes with the stream iterators and algorithms.
Using Manipulators To Control Object Format
Objects can be streamed to and from virtually any I/O device that can be connected to the computer. In many situations, it's advantageous to move objects directly from a stream to an STL container. Likewise, it's often useful to move objects that are stored in STL containers to an iostream of some type. For instance, if a file contains a collection of objects that need to be sorted, one method of sorting those objects would be to read them into memory, store them in a container, and then sort the container's contents. In this case, it would save some effort if we could stream the objects directly into a sorted container. Or, if we want to save a collection of objects for future processing, it would be convenient to be able to stream the collection directly to a file.
Streaming to and from containers is straightforward for the built-in types, but we would like to be able to stream our own user-defined objects. Further, the supplier of a class must realize that users of that class will expect objects of that type to be compatible with the standard I/O, container, and algorithm facilities in C++. Because streaming is fundamental in object-oriented C++ programming, it's almost a necessity to design your classes and objects with streaming in mind. If your classes define the necessary operators, it will be easy for the user of your objects to stream them to and from STL containers.
For user-defined objects, the streaming process typically requires three steps:
Translation.
Formatting.
Writing/reading the actual I/O device or device buffer.
As we explained in part 1 of this series, insertion is the process of putting objects into the stream. Extraction is the process of removing objects from the stream. During object insertion, the object is translated from its native format to simpler built-in types. The built-in types are then properly formatted for the output device and inserted into the output stream. During extraction, the reverse happens. The object's components are retrieved from the output device, formatted as simple built-in types, and then translated into the object's native format. Figure 1 shows the streaming process for insertion; Figure 2 shows the streaming process for extraction.
Figure 1 The streaming process for inserting user-defined objects.
Figure 2 The streaming process for extracting user-defined objects.
Notice in the figures that the ostream_iterator and the istream_iterator use the << insertion operator and the >> extraction operator, respectively. These operators manage the streaming process. The implementations of these operators for the user-defined class will take advantage of or set any necessary manipulators and then they will ultimately call the functions that interact with the I/O device. It's important to have this process clear because the stream iterators that connect the iostreams to the STL containers and algorithms require the insertion and extraction operators to be defined for user-defined objects. Further, since the stream iterators work with istream classes, ostream classes, and their descendants, the user can take advantage of any built-in manipulators or user-defined manipulators to format the objects as desired prior to inserting them into or extracting them from the stream.
Table 1 shows where inserters, extractors, and manipulators fit in the streaming process.
Operator/Method |
Process |
Associated Stream Classes |
<<, >> |
Translation |
basic_istream basic_ostream basic_ifstream basic_ofstream basic_istrstream basic_ostrstream |
manipulators setw, setf, hex, fill() scientific(), etc. |
Formatting |
ios_base basic_ios |
write, read, put, get |
Actual I/O processing |
basic_streambuf basic_filebuf stringstreambuf |
To take advantage of streaming, the supplier or designer has to do a little extra work up front, but the payoff is considerable as the class is used and reused.