The software industry is now a healthy, worldwide economic market, and applications that can run in various languages and cultures are in demand. As early as the late 1980s, the C Standards Committee added support for non-U.S. formatting conventions with their
Wide Streams
A
template
class basic_istream {…};
In fact, all input stream types are specializations of this template, according to the following type definitions:
typedef basic_istream
typedef basic_istream
typedef basic_ifstream
typedef basic_ifstream
typedef basic_istringstream
typedef basic_istringstream
All other stream types are defined in similar fashion.
In a "perfect" world, this is all you’d have to do to have streams of different character types. In reality, things aren’t that simple. The reason is that the character-processing functions provided for char and wchar_t don’t have the same names. To compare two narrow strings, for example, you use the strcmp( ) function. For wide characters, that function is named wcscmp( ). (Remember these originated in C, which does not have function overloading, hence unique names are a must.) For this reason, a generic stream can’t just call strcmp( ) in response to a comparison operator. There needs to be a way for the correct low-level functions to be called automatically.
The principle that guides the solution is well known. You simply "factor out" the differences into a new abstraction. The operations you can perform on characters have been abstracted into the char_traits template, which has predefined specializations for char and wchar_t, as we discussed at the end of the previous chapter. To compare two strings, then, basic_string just calls traits::compare( ) (remember that traits is the second template parameter), which in turn calls either strcmp( ) or wcscmp( ), depending on which specialization is being used (transparent to basic_string, of course).
You only need to be concerned about char_traits if you access the low-level character processing functions; most of the time you don’t care. Consider, however, making your inserters and extractors more robust by defining them as templates, just in case someone wants to use them on a wide stream.
To illustrate, recall again the Date class inserter from the beginning of this chapter. We originally declared it as:
ostream& operator<<(ostream&, const Date&);
This accommodates only narrow streams. To make it generic, we simply make it a template based on basic_ostream:
template
std::basic_ostream
operator<<(std::basic_ostream
const Date& d) {
charT fillc = os.fill(os.widen('0'));
charT dash = os.widen('-');
os << setw(2) << d.month << dash
<< setw(2) << d.day << dash
<< setw(4) << d.year;
os.fill(fillc);
return os;
}
Notice that we also have to replace char with the template parameter charT in the declaration of fillc, since it could be either char or wchar_t, depending on the template instantiation being used.