Follow Techotopia on Twitter

On-line Guides
All Guides
eBook Store
iOS / Android
Linux for Beginners
Office Productivity
Linux Installation
Linux Security
Linux Utilities
Linux Virtualization
Linux Kernel
System/Network Admin
Programming
Scripting Languages
Development Tools
Web Development
GUI Toolkits/Desktop
Databases
Mail Systems
openSolaris
Eclipse Documentation
Techotopia.com
Virtuatopia.com
Answertopia.com

How To Guides
Virtualization
General System Admin
Linux Security
Linux Filesystems
Web Servers
Graphics & Desktop
PC Hardware
Windows
Problem Solutions
Privacy Policy

  




 

 

Thinking in C++ Vol 2 - Practical Programming
Prev Home Next

Inserters and extractors

A stream is an object that transports and formats characters of a fixed width. You can have an input stream (via descendants of the istream class), an output stream (with ostream objects), or a stream that does both simultaneously (with objects derived from iostream). The iostreams library provides different types of such classes: ifstream, ofstream, and fstream for files, and istringstream, ostringstream, and stringstream for interfacing with the Standard C++ string class. All these stream classes have nearly identical interfaces, so you can use streams in a uniform manner, whether you re working with a file, standard I/O, a region of memory, or a string object. The single interface you learn also works for extensions added to support new classes. Some functions implement your formatting commands, and some functions read and write characters without formatting.

The stream classes mentioned earlier are actually template specializations,[41] much like the standard string class is a specialization of the basic_string template. The basic classes in the iostreams inheritance hierarchy are shown in the following figure:

The ios_base class declares everything that is common to all streams, independent of the type of character the stream handles. These declarations are mostly constants and functions to manage them, some of which you ll see throughout this chapter. The rest of the classes are templates that have the underlying character type as a parameter. The istream class, for example, is defined as follows:

typedef basic_istream<char> istream;
 

All the classes mentioned earlier are defined via similar type definitions. There are also type definitions for all stream classes using wchar_t (the wide character type discussed in Chapter 3) instead of char. We ll look at these at the end of this chapter. The basic_ios template defines functions common to both input and output, but that depends on the underlying character type (we won t use these much). The template basic_istream defines generic functions for input, and basic_ostream does the same for output. The classes for file and string streams introduced later add functionality for their specific stream types.

In the iostreams library, two operators are overloaded to simplify the use of iostreams. The operator << is often referred to as an inserter for iostreams, and the operator >> is often referred to as an extractor.

Extractors parse the information that s expected by the destination object according to its type. To see an example of this, you can use the cin object, which is the iostream equivalent of stdin in C, that is, redirectable standard input. This object is predefined whenever you include the <iostream> header.

int i;
cin >> i;
 
float f;
cin >> f;
 
char c;
cin >> c;
 
char buf[100];
cin >> buf;
 

There s an overloaded operator >> for every built-in data type. You can also overload your own, as you ll see later.

To find out what you have in the various variables, you can use the cout object (corresponding to standard output; there s also a cerr object corresponding to standard error) with the inserter <<:

cout << "i = ";
cout << i;
cout << "\n";
cout << "f = ";
cout << f;
cout << "\n";
cout << "c = ";
cout << c;
cout << "\n";
cout << "buf = ";
cout << buf;
cout << "\n";
 

This is tedious and doesn t seem like much of an improvement over printf( ), despite improved type checking. Fortunately, the overloaded inserters and extractors are designed to be chained into a more complex expression that is much easier to write (and read):

cout << "i = " << i << endl;
cout << "f = " << f << endl;
cout << "c = " << c << endl;
cout << "buf = " << buf << endl;
 

Defining inserters and extractors for your own classes is just a matter of overloading the associated operators to do the right things, namely:

      Make the first parameter a non-const reference to the stream (istream for input, ostream for output).

      Perform the operation by inserting/extracting data to/from the stream (by processing the components of the object).

      Return a reference to the stream.

The stream should be non-const because processing stream data changes the state of the stream. By returning the stream, you allow for chaining stream operations in a single statement, as shown earlier.

As an example, consider how to output the representation of a Date object in MM-DD-YYYY format. The following inserter does the job:

ostream& operator<<(ostream& os, const Date& d) {
char fillc = os.fill('0');
os << setw(2) << d.getMonth() << '-'
<< setw(2) << d.getDay() << '-'
<< setw(4) << setfill(fillc) << d.getYear();
return os;
}
 

This function cannot be a member of the Date class because the left operand of the << operator must be the output stream. The fill( ) member function of ostream changes the padding character used when the width of an output field, determined by the manipulator setw( ), is greater than needed for the data. We use a 0 character so that months preceding October will display a leading zero, such as 09 for September. The fill( ) function also returns the previous fill character (which defaults to a single space) so that we can restore it later with the manipulator setfill( ). We discuss manipulators in depth later in this chapter.

Extractors require a little more care because things can go wrong with input data. The way to signal a stream error is to set the stream s fail bit, as follows:

istream& operator>>(istream& is, Date& d) {
is >> d.month;
char dash;
is >> dash;
if(dash != '-')
is.setstate(ios::failbit);
is >> d.day;
is >> dash;
if(dash != '-')
is.setstate(ios::failbit);
is >> d.year;
return is;
}
 

When an error bit is set in a stream, all further streams operations are ignored until the stream is restored to a good state (explained shortly). That s why the code above continues extracting even if ios::failbit gets set. This implementation is somewhat forgiving in that it allows white space between the numbers and dashes in a date string (because the >> operator skips white space by default when reading built-in types). The following are valid date strings for this extractor:

"08-10-2003"
"8-10-2003"
"08 - 10 - 2003"
 

but these are not:

"A-10-2003" // No alpha characters allowed
"08%10/2003" // Only dashes allowed as a delimiter
 

We ll discuss stream state in more depth in the section Handling stream errors later in this chapter.

Thinking in C++ Vol 2 - Practical Programming
Prev Home Next

 
 
   Reproduced courtesy of Bruce Eckel, MindView, Inc. Design by Interspire