开发者

C++ Decorate basic_stream::underflow()

I want to extend the behavior of a basic_streambuf object by using the decorator pattern. That`s what I currently got:

template<typename char_type, class traits_type>
class forwarding_basic_streambuf
    : boost::noncopyable,
      public std::basic_streambuf<char_type, traits_type>
{
public:
    typedef std::basic_streambuf<char_type, traits_type> forwarded_type;

    forwarding_basic_streambuf(forwarded_type& fwd_buf)
        : m_fwd(&fwd_buf) { }
    virtual ~forwarding_basic_streambuf() { }

    // locales:
//  std::locale pubimbue(std::locale const& loc);
//      => Calls: imbue(loc) | Returns: Previous value of getloc();
//  std::locale getloc  () const;
//      => Returns: If pubimbue() has ever been called, then the last value of loc supplied, otherwise the
//                  current global locale, locale(), in effect at the time of construction. If called after
//                  pubimbue() has been called but before pubimbue has returned (i.e., from within the call
//                  of imbue()) then it returns the previous value.

    // buffer management and positioning:
//  forwarded_type* pubsetbuf (char_type* s, std::streamsize n); => Returns: setbuf(s, n)
//  pos_type        pubseekoff(off_type off, std::ios_base::seekdir way,
//      std::ios_base::openmode which = std::ios_base::in | std::ios_base::out);
//      => Returns seekoff(off, way, which)
//  pos_type        pubseekpos(pos_type sp,
//      std::ios_base::openmode which = std::ios_base::in | std::ios_base::out);
//      => Returns: seekpos(sp, which)
//  int             pubsync   (); => Returns: sync()

    // get and put areas:
    // get area:
//  std::streamsize sgetn   (char_type* s, std::streamsize n); => Returns: xsgetn(s, n)

    // put area:
//  std::streamsize sputn(char_type const* s, std::streamsize n); => Returns: xsputn(s, n)

protected:
    // virtual functions:
    // locales:
    virtual void imbue(std::locale const& loc) { this->m_fwd->pubimbue(loc); }

    // buffer management and positioning:
    virtual forwarded_type*  setbuf (char_type* s, std::streamsize n)
        { return this->m_fwd->pubsetbuf(s, n); }
    virtual pos_type         seekoff(off_type off, std::ios_base::seekdir way,
        std::ios_base::openmode which = std::ios_base::in | std::ios_base::out)
        { return this->m_fwd->pubseekoff(off, way); }
    virtual pos_type         seekpos(pos_type sp,
        std::ios_base::openmode which = std::ios_base::in | std::ios_base::out)
        { return this->m_fwd->pubseekpos(sp, which); }
    virtual int              sync   ()
        { return this->m_fwd->pubsync(); }

    // get and put areas:
    // get area:
    virtual std::streamsize xsgetn(char_type* s, std::streamsize n)
        { return this开发者_运维技巧->m_fwd->sgetn(s, n); }
    virtual int_type        uflow()
        {
            if (traits_type::eq_int_type(this->underflow(), traits_type::eof()))
                return traits_type::eof();
            return this->m_fwd->sgetc();
        }

    // put area:
    virtual std::streamsize xsputn  (char_type const* s, std::streamsize n)
        { return this->m_fwd->sputn(s, n); }
    virtual int_type        overflow(int_type c = traits_type::eof())
        {
            if (traits_type::eq_int_type(c, traits_type::eof()))
                return traits_type::not_eof(c);
            return this->m_fwd->sputc(traits_type::to_char_type(c));
        }

private:
    forwarded_type* m_fwd;
};

The basic goal (as a first step) would be to simply forward each functionality to the decorated object. Thus, it should be possible to use this decorater even with a pointer to it`s base class.

That all works fine for writing methods, but I don`t know how to deal with the underflow() function, which is called from uflow() and sgetc().


I might be missing something but your design doesn't make much sense to me.

As I see it, extending a streambuf is done by overriding the protected virtual interface, if you'd want to use a decorator pattern your decorator base class would do just that.

template<typename char_type, class traits_type>
class forwarding_basic_streambuf
    : boost::noncopyable,
      public std::basic_streambuf<char_type, traits_type>
{
public:
    typedef std::basic_streambuf<char_type, traits_type> forwarded_type;

    forwarding_basic_streambuf(forwarded_type& fwd_buf)
        : m_fwd(&fwd_buf) { }
    virtual ~forwarding_basic_streambuf() { }

protected:
   virtual streamsize xsputn ( const char * s, streamsize n ) {
       m_fwd->xsputn(s,n);
   }
   virtual int overflow ( int c) {
       m_fwd->overflow(c);
   }
   // etc.
};

You real implementation would then add any needed 'decorations' e.g.

template<typename char_type, class traits_type>
class add_timestamp_decorator
    : public forwarding_basic_streambuf<char_type, traits_type>
{
public:
    typedef std::forwarding_basic_streambuf<char_type, traits_type> base_type;

    add_timestamp_decorator(base_type::forwarded_type& fwd_buf)
        : base_type(&fwd_buf) { }
    virtual ~add_timestamp_decorator() { }

protected:
   virtual streamsize xsputn ( const char * s, streamsize n ) {
       // detect and remember when a newline is written
       // before the next char is written output the timestamp
       base_type::xsputn(s, n);
   }
   // etc.
};

And then use it on an outputstream (pseudo code, omitted template definitions for clarity)

ostream outputstream;
// ....
add_timestamp_decorator decorator = new add_timestamp_decorator(outputstream.rdbuf());
outputstream.rdbuf(decorator);

outputstream << "some lines\ntimestamps will be inserted\n\n";


The specification describes uflow() in the following way (27.6.3.4.3.[15-17]):

  • Requires: The constraints are the same as for underflow(), except that the result character be transferred from the pending sequence to the backup sequence, and the pending sequence shall not be empty before the transfer.
  • Default behavior: Calls underflow(). If underflow() returns traits::eof(), returns traits::eof(). Otherwise, returns the value of traits::to_int_type(*gptr()) and increment the value of the next pointer for the input sequence.
  • Returns: traits::eof() to indicate failure.

In C++, this look would look like:

if (traits::eq_int_type(this->underflow(), traits::eof())
    return traits::eof();    
return *fetch_gptr_and_inc();

We cannot call underflow() on the decorated object directly, because it's not accessible. In addition, we don't want to fetch our own gptr(), but the decorated one. However, we can achieve this indirectly by calling sgetc() and sbumpc:

template<typename char_type, class traits_type>
typename forwarding_basic_streambuf<char_type, traits_type>::int_type
forwarding_basic_streambuf<char_type, traits_type>::uflow()
{   
    if (traits_type::eq_int_type(this->m_fwd->sgetc(), traits_type::eof()))
        return traits_type::eof();
    return this->m_fwd->sbumpc();
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜