Apache C++ Standard Library User's Guide

## 36.3 Another Look at the Date Format String

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`).