Extractors require a little more care because things sometimes 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.
Common usage
As the Date extractor illustrated, you must be on guard for erroneous input. If the input produces an unexpected value, the process is skewed, and it’s difficult to recover. In addition, formatted input defaults to white space delimiters. Consider what happens when we collect the code fragments from earlier in this chapter into a single program:.
//: C04:Iosexamp.cpp
// Iostream examples
#include <iostream>
using namespace std;
int main() {
int i;
cin >> i;
float f;
cin >> f;
char c;
cin >> c;
char buf[100];
cin >> buf;
cout << "i = " << i << endl;
cout << "f = " << f << endl;
cout << "c = " << c << endl;
cout << "buf = " << buf << endl;
cout << flush;
cout << hex << "0x" << i << endl;
} ///:~
and give it the following input:.
12 1.4 c this is a test
We expect the same output as if we gave it:
12
1.4
c
this is a test
but the output is, somewhat unexpectedly
i = 12
f = 1.4
c = c
buf = this
0xc
Notice that buf got only the first word because the input routine looked for a space to delimit the input, which it saw after "this." In addition, if the continuous input string is longer than the storage allocated for buf, we overrun the buffer.
In practice, you’ll usually want to get input from interactive programs a line at a time as a sequence of characters, scan them, and then perform conversions once they’re safely in a buffer. This way you don’t have to worry about the input routine choking on unexpected data.
Another thing to consider is the whole concept of a command-line interface. This made sense in the past when the console was little more than a glass typewriter, but the world is rapidly changing to one in which the graphical user interface (GUI) dominates. What is the meaning of console I/O in such a world? It makes much more sense to ignore cin altogether, other than for simple examples or tests, and take the following approaches:.
1.If your program requires input, read that input from a file—you’ll soon see that it’s remarkably easy to use files with iostreams. Iostreams for files still works fine with a GUI.
2.Read the input without attempting to convert it, as we just suggested. When the input is some place where it can’t foul things up during conversion, you can safely scan it.
3.Output is different. If you’re using a GUI, cout doesn’t necessarily work, and you must send it to a file (which is identical to sending it to cout) or use the GUI facilities for data display. Otherwise it often makes sense to send it to cout. In both cases, the output formatting functions of iostreams are highly useful.
Another common practice saves compile time on large projects. Consider, for example, how you would declare the Date stream operators introduced earlier in the chapter in a header file. You only need to include the prototypes for the functions, so it’s not really necessary to include the entire <iostream> header in Date.h. The standard practice is to only declare classes, something like this:.
class ostream;
This is an age-old technique for separating interface from implementation and is often called a forward declaration (and ostream at this point would be considered an incomplete type, since the class definition has not yet been seen by the compiler).
This will not work as is, however, for two reasons:
1. The stream classes are defined in the std namespace.
2. They are templates.
The proper declaration would be:
namespace std {
template<class charT, class traits = char_traits<charT> >
class basic_ostream;
typedef basic_ostream<char> ostream;
}
(As you can see, like the string class, the streams classes use the character traits classes mentioned in Chapter 3). Since it would be terribly tedious to type all that for every stream class you want to reference, the standard provides a header that does it for you: <iosfwd>. The Date header would then look something like this:.
// Date.h
#include <iosfwd>
class Date {
friend std::ostream& operator<<(std::ostream&,
const Date&);
friend std::istream& operator>>(std::istream&, Date&);
// etc.
Line-oriented input