The format of dates depends on local and cultural conventions. Naturally, we want our extractor and inserter to parse and format the date according to such conventions. To add this functionality to them, we use the time facet contained in the respective stream's locale as follows:
template<class charT, class Traits> std::basic_istream<charT, Traits>& operator >> (std::basic_istream<charT, Traits >& is, date& dat) { std::ios_base::iostate err = 0; std::use_facet<std::time_get<charT,Traits> > //1 (is.getloc()) .get_date(is, std::istreambuf_iterator<charT,Traits>(), //2 is, err, &dat.tm_date); //3 return is; }
//1 | Use the std::time_get facet of the input stream's locale to handle parsing of dates according to cultural conventions defined by the locale. The locale in question is obtained through the stream's getloc() member function. Its std::time_get facet is accessed through a call to the function template std::use_facet<...>(). The type argument to the use_facet() function template is the facet type. (See the chapter on internationalization for more details on locales and facets.). |
//2 | The facet's member function get_date() is called. It takes a number of arguments, including:
A range of input iterators. For the sake of performance and efficiency, facets directly operate on a stream's buffer. They access the stream buffer through stream buffer iterators. (See Chapter 43, "Stream Iterators.") Following the philosophy of iterators in the C++ Standard Library, we must provide a range of iterators. The range extends from the iterator pointing to the first character to be accessed, to the character past the last character to be accessed (the past-the-end-position). The beginning of the input sequence is provided as a reference to the stream. The std::istreambuf_iterator class template has a constructor taking a reference to an input stream. Therefore, the reference to the stream is automatically converted into an istreambuf_iterator that points to the current position in the stream. As end of the input sequence, an end-of-stream iterator is provided. It is created by the default constructor of istreambuf_iterator. With these two stream buffer iterators, the input is parsed from the current position in the input stream until a date or an invalid character is found, or the end of the input stream is reached. |
//3 | The other parameters are:
Formatting flags. A reference to the ios_base part of the stream object is provided here, so that the facet can use the stream's formatting information through the stream's members flags(), precision(), and width(). An iostream state. It is used for reporting errors while parsing the date. A pointer to a time object. It must be a pointer to an object of type std::tm, which is the time structure defined by the C library. Our date class maintains such a time structure, so we hand over a pointer to the respective data member tm_date. |
The inserter is built analogously:
template<class charT, class Traits> std::basic_ostream<charT, Traits>& operator << (std::basic_ostream<charT, Traits >& os, const date& dat) { std::use_facet <std::time_put<charT,ostreambuf_iterator<charT,Traits> > > //1 os.getloc()) .put(os,os,os.fill(),&dat.tm_date,'x'); //2 return os; }
Note how these versions of the inserter and extractor differ from previous simple versions: we no longer rely on existing inserters and extractors for built-in types, as we did when we used operator<<(int) to insert the date object's data members individually. Instead, we use a low-level service like the time facet's get_date() service. The consequence is that we give away all the functionality that high-level services like the inserters and extractors already provide, such as format control, error handling, etc.
The same happens if you decide to access the stream's buffer directly, perhaps for optimizing your program's runtime efficiency. The stream buffer's services, too, are low-level services that leave to you the tasks of format control, error handling, etc.
In the following sections, we explain how you can improve and complete your inserter or extractor if it directly uses low-level components like locales or stream buffers.