We would like to store the date format string in the iostream storage through std::ios_base::iword() and std::ios_base::pword(). In this way, the input and output operations for date objects can access the format string for parsing and formatting. Format parameters are often set with manipulators (see Section 28.3.2), so we should add a manipulator that sets the date format string. It could be used like this:
date today; std::ofstream ostr; // ... ostr << setfmt("%D") << today;
Here is a suggested implementation for such a manipulator (note that the name of the base class is implementation-defined):
class setfmt : public smanip<const char*> { public: setfmt(const char* fmt) : smanip<const char*>(setfmt_,fmt) {} private: static const int datfmtIdx; //1 static std::ios_base& setfmt_(std::ios_base& str, const char* fmt) { str.pword(datfmtIdx) = const_cast<char*> (fmt); //2 return str; } template<class charT, class Traits> friend std::basic_ostream<charT, Traits>& //3 operator<<(basic_ostream<charT, Traits>& os, const date& dat); }; const int setfmt::datfmtIdx = std::ios_base::xalloc(); //4
The technique applied to implement the manipulator is described in detail in Example 2 of Section 33.3, so we won't repeat it here. But regarding this manipulator and the private use of iostream storage, there are other interesting details:
//1 | The manipulator class owns the index of the element in the iostream storage where we want to store the format string. It is initialized in //4 by a call to xalloc(). |
//2 | The manipulator accesses the array pword() using the index datfmtIdx, and stores the pointer to the date format string. [For brevity, error handling is omitted in the example. If allocation fails, then std::ios_base::badbit is set.] Note that the reference returned by pword() is only used for storing the pointer to the date format string. Generally, you should never store a reference returned by iword() or pword() in order to access the stored data through this reference later on. This is because these references can become invalid once the array is reallocated or copied. (See the Apache C++ Standard Library Reference Guide for more details.) |
//3 | The inserter for date objects needs to access the index into the array of pointers, so that it can read the format string and use it. Therefore, the inserter must be declared as a friend. In principle, the extractor must be a friend, too; however, the standard C++ locale falls short of supporting the use of format strings like the ones used by the standard C function strptime(). Hence, the implementation of a date extractor that supports date format strings would be a lot more complicated than the implementation for the inserter, which can use the stream's locale. We have omitted the extractor for the sake of brevity. |
//4 | Initializes the index of elements in istream storage where the format string is kept. |
The inserter for date objects given below is almost identical to the one we described in Section 32.5.1:
template<class charT, class Traits> std::basic_ostream<charT, Traits> & operator << (std::basic_ostream<charT, Traits >& os, const date& dat) { std::ios_base::iostate err = 0; charT* fmt = 0; try { typename std::basic_ostream<charT, Traits>::sentry opfx(os); if(opfx) { char* patt = static_cast<char*> (os.pword(setfmt.datfmtIdx); //1 std::size_t len = std::strlen(patt); fmt = new charT[len]; std::use_facet<std::ctype<charT> >(os.getloc()). widen(patt, patt+len, fmt); if (std::use_facet<std::time_put<charT, std::ostreambuf_iterator<charT,Traits> > > (os.getloc()) .put(os,os,os.fill(),&dat.tm_date,fmt,fmt+len) //2 .failed() ) err = std::ios_base::badbit; os.width(0); } } catch(...) { delete [] fmt; bool flag = false; try { os.setstate(std::ios_base::failbit); } catch(std::ios_base::failure) { flag = true; } if (flag) throw; } delete [] fmt; if ( err ) os.setstate(err); return os; }
The only change from the previous inserter is that the format string here is read from the iostream storage (in statement //1) instead of being the fixed string "%x". The format string is then provided to the locale's time formatting facet (in statement //2).