Apache C++ Standard Library User's Guide

## 38.4 The Example

Let's return now to our example, in which we are creating a new stream template by derivation.

### 38.4.1 The Derived Stream Template

Let us derive a new stream type `odatstream` that has an additional data member `fmt_` for storing a date format string, together with a corresponding member function `fmt()` for setting the date format specification.

    template <class charT, class Traits=std::char_traits<charT> >
    class odatstream : public std::basic_ostream <charT,Traits>
    {
    public:
      odatstream(std::basic_ostream<charT,Traits>& ostr,
                 const char* fmt = "%x")                            //1
        : std::basic_ostream<charT,Traits>(ostr.rdbuf())
      {
        fmt_=new charT[std::strlen(fmt)];
        std::use_facet<std::ctype<charT> >(ostr.getloc()).
          widen(fmt, fmt+std::strlen(fmt), fmt_);                   //2
      }

      std::basic_ostream<charT,Traits>& fmt(const char* f)          //3
      {
        delete[] fmt_;
        fmt_=new charT[std::strlen(f)];
        std::use_facet<std::ctype<charT> >(os.getloc()).
          widen(f, f+std::strlen(f), fmt_);
        return *this;
      }

      charT const* fmt() const                                      //4
      {
        charT * p = new charT[Traits::length(fmt_)];
        Traits::copy(p,fmt_,Traits::length(fmt_));
        return p;
      }
      ~odatstream() {                                               //5
        delete[] fmt_; }

    private:
      charT* fmt_;                                                  //6

    template <class charT, class Traits>                            //7
     friend std::basic_ostream<charT, Traits> &
     operator << (std::basic_ostream<charT, Traits >& os,
       const date& dat);
    };

`//1` | A date output stream borrows the stream buffer of an already existing output stream, so that the two streams share the stream buffer. The constructor also takes an optional argument, the date format string. This is always a sequence of narrow characters.
`//2` | The format string is widened or translated into the stream's character type `charT`. This is because the format string is provided to the time facet of the stream's locale, which expects an array of characters of type `charT`.
`//3` | This version of function `fmt()` allows you to set the format string.
`//4` | This version of function `fmt()` returns the current format string setting.
`//5` | The date stream class needs a destructor that deletes the format string.
`//6` | A pointer to the date format specification is stored as a private data member `fmt_`.
`//7` | The inserter for dates must access the date format specification. For this reason, we make it a friend of class `odatstream`.

### 38.4.2 The Date Inserter

We would like to be able to insert `date` objects into all kinds of output streams. Whenever the output stream is a date output stream of type `odatstream`, we would also like to take advantage of its ability to carry additional information for formatting date output. How can this be achieved?

It would be ideal if the inserter for `date` objects were a virtual member function of all output stream classes that we could implement differently for different types of output streams. For example, when a date object is inserted into an `odatstream,` the formatting would use the available date formatting string; when inserted into an arbitrary output stream, default formatting would be performed. Unfortunately, we cannot modify the existing output stream classes, since they are part of a library you will not want to modify.

This kind of problem is typically solved using dynamic casts. Since the stream classes have a virtual destructor, inherited from class `std::basic_ios`, we can use dynamic casts to achieve the desired virtual behavior.

> NOTE -- For a more detailed discussion of the problem and its solution, see Section 14.2, p. 306ff, of Bjarne Stroustrup, The Design and Evolution of C++, Addison-Wesley 1994.

Here is the implementation of the date inserter:

    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 = std::ios_base::goodbit;

      try {
        typename std::basic_ostream<charT,
                                    Traits>::sentry opfx(os);

        if (opfx) {
          charT buf[3];
          const charT *fmt = buf;

          odatstream<charT, Traits> *p =
            dynamic_cast<odatstream<charT, Traits>*>(&os);     //1

          if (p)
            fmt = p->fmt_;                                     //2
          else {                                               //3
            char patt[3] = "%x";
            std::use_facet<ctype<charT> >(os.getloc ())
              .widen (patt, patt + 3, buf);
          }

          typedef std::ostreambuf_iterator<charT, Traits>
            Iterator;
          typedef std::time_put<charT, Iterator> TimePut;

          if (std::use_facet<TimePut>(os.getloc())
              .put (os, os, os.fill (), &dat.tm_date_,
                    fmt, fmt + Traits::length (fmt)).failed())
            err = ios_base::badbit;
          os.width (0);
        }
      }
      catch (...) {
        bool flag = false;
        try {
          os.setstate (std::ios_base::failbit);
        }
        catch (std::ios_base::failure) {
          flag = true;
        }
        if (flag)
          throw;
      }
      if (err)
        os.setstate(err);

      return os;
    }

`//1` | Use `dynamic_cast<>()` to determine whether the stream object `os` is of the expected type.
`//2` | If `os` is of the `odatstream` type (or publicly derived from it), use the `fmt_` member.
`//3` | Otherwise, use the `"%x"` format specifier.

### 38.4.3 The Manipulator

The date output stream has a member function for setting the format specification. Analogous to the standard stream format functions, we would like to provide a manipulator for setting the format specification. This manipulator affects only output streams. Therefore, we must define a manipulator base class for output stream manipulators, `osmanip`, along with the necessary inserter for this manipulator. We do this in the code below. See Section 33.3.3 for a detailed discussion of the technique we are using here:

    template <class Ostream, class Arg>
    class osmanip {
      public:
        osmanip(Ostream& (*pf)(Ostream&, Arg), Arg arg)
        : pf_(pf) , arg_(arg) { ; }

      protected:
        Ostream&     (*pf_)(Ostream&, Arg);
        Arg          arg_;
        friend Ostream& operator<< (Ostream& ostr,
          const osmanip &manip) {
            return (*manip.pf_)(os, manip.arg_);
        }
    };

After these preliminaries, we can now implement the `setfmt` manipulator itself:

    template <class charT, class Traits>
    inline std::basic_ostream<charT,Traits>&
    sfmt(std::basic_ostream<charT,Traits>& os, const char* f)    //1
    {
      odatstream<charT,Traits>* p =
        dynamic_cast<odatstream<charT,Traits>*>(&os);            //2

      if (p)
        p->fmt(f);                                               //3

      return os;                                                 //4
    }

    template <class charT,class Traits>
    inline osmanip<std::basic_ostream<charT,Traits>,const char*>
    setfmt(const char* fmt)                                     //5
    {
      return osmanip<std::basic_ostream<charT,Traits>,
          const char*>(sfmt,fmt);
    }

`//1` | The function `sfmt()` is the function associated with the `setfmt` manipulator. Its task is to take a format specification and hand it over to the stream. This happens only if the stream is a date output stream; otherwise, nothing is done.
`//2` | We determine the stream's type through a dynamic cast.
`//3` | In case the stream actually is a date output stream, we store the format specification by calling the stream's `fmt()` function.
`//4` | Otherwise, do nothing.
`//5` | The manipulator itself is a function that creates an output manipulator object. Since neither of its template arguments can be deduced, they need to be explicitly specified.

### 38.4.4 A Remark on Performance

The solution suggested in Section 38.4.3 uses dynamic casts and exception handling to implement the date inserter and the date format manipulator. Although this technique is elegant and makes proper use of the C++ language, it might introduce some degradation in runtime performance due to the use RTTI (Run-Time Type Identification).

If optimal performance is important, you can choose an alternative approach: in the proposed solution that uses dynamic casts, extend the date inserter for arbitrary output streams `basic_ostream<charT,Traits>& operator<< (basic_ostream <charT,Traits>&, const date&)` so that it formats dates differently, depending on the type of output stream. Alternatively, you can leave the existing date inserter for output streams unchanged and implement an additional date inserter that works for output date streams only; its signature would be `odatstream<charT,Traits>& operator<< (odatstream<charT,Traits>&, const date&)`. Also, you would have two manipulator functions, one for arbitrary output streams and one for output date streams only, that is, `basic_ostream<charT,Traits>& sfmt(basic_ostream<charT,Traits>&, const char*)` and `odatstream<charT,Traits>& sfmt
(odatstream<charT,Traits>&, const char*)`. In each of the functions for date streams, you would replace those operations that are specific for output date streams.

This technique has the drawback of duplicating most of the inserter's code, which in turn might introduce maintenance problems. The advantage is that the runtime performance is unlikely to be adversely affected.