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;
}
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.