简体   繁体   中英

C++ Decorate basic_iostream classes

I want to do something like the following code shows:

class foo
{
private:
    std::fstream* m_stream;

public:
    foo(std::fstream* stream) : m_stream(stream) { }

    foo& write(char const* s, std::streamsize count)
    {
        if (/*condition*/)
        {
            m_stream->write(s, count);
        }
        else
        {
            // ...
        }

        return *this;
    }

    foo& read(char* s, std::streamsize count)
    {
        if (/*condition*/)
        {
            m_stream->read(s, count);
        }
        else
        {
            // ...
        }

        return *this;
    }
};

I would need to add the same behavior to all similar methods (eg put ). This shouldn't be applied to file streams only, but all other stream classes. Is there any easy way to allow these functionality?

Many of the formatted output operators ( operator<< ) write directly to the underlying stream buffer. What you need to do in order to accomplish this in a general fashion is derive a class from std::basic_streambuf that forwards all data to another std::basic_streambuf, and then optionally create a minimal std::basic_ostream implementation to make using your stream buffer easier.

I wouldn't say this is particularly easy, though, but it's the only way to do this in a way that can affect all stream types.

Here is an example of a minimal stream buffer that forwards to another stream buffer (and performs some meaningless transformation just to demonstrate what you can do), and an accompanying stream:

#include <iostream>
#include <streambuf>

template<typename CharType, typename Traits = std::char_traits<CharType> >
class ForwardingStreamBuf : public std::basic_streambuf<CharType, Traits>
{
public:
    typedef Traits traits_type;
    typedef typename traits_type::int_type int_type;
    typedef typename traits_type::pos_type pos_type;
    typedef typename traits_type::off_type off_type;

    ForwardingStreamBuf(std::basic_streambuf<CharType, Traits> *baseStreamBuf)
        : _baseStreamBuf(baseStreamBuf)
    {
    }

protected:
    virtual int_type overflow(int_type c = traits_type::eof())
    {
        if( _baseStreamBuf == NULL )
            return traits_type::eof();

        if( traits_type::eq_int_type(c, traits_type::eof()) )
            return traits_type::not_eof(c);
        else
        {
            CharType ch = traits_type::to_char_type(c);
            if( ch >= 'A' && ch <= 'z' )
                ch++; // Do some meaningless transformation
            return _baseStreamBuf->sputc(ch);
        }
    }

    virtual int sync()
    {
        if( _baseStreamBuf == NULL )
            return -1;
        else
            return _baseStreamBuf->pubsync();
    }
private:
    std::basic_streambuf<CharType, Traits> *_baseStreamBuf;
};

template<typename CharType, typename Traits = std::char_traits<CharType> >
class ForwardingStream : public std::basic_ostream<CharType, Traits>
{
public:
    ForwardingStream(std::basic_ostream<CharType, Traits> &stream)
        : std::basic_ostream<CharType, Traits>(NULL), _buffer(stream.rdbuf())
    {
        this->init(&_buffer);
    }

    ForwardingStreamBuf<CharType, Traits>* rdbuf() const
    {
        return &_buffer;
    }
private:
    ForwardingStreamBuf<CharType, Traits> _buffer;
};

This can be used very simply:

int main()
{
    ForwardingStream<char> test(std::cout);
    test << "Foo" << std::endl;
}

Which would output Gpp . I hope that helps you on your way.

Something like this?

   template <class Stream>
    class DecoratedStream {
    public:
      DecoratedStream(Stream* stream) : m_stream(stream) {}

      DecoratedStream& write(const char* data, int count) {
        m_stream->write(data, count);
      }

    };

If I understand you correctly, you want to decorate methods of any iostream . So just make your decorator take an iostream as decoratee (as opposed to an fstream , which is a subclass of iostream ).

Having pointer inside a structure as your current approach is dangerous and error prone. Instead just derive such stream classes and implement basic constructors and wrap around your custom methods such as write() .

template<typename StreamType>
class foo : StreamType
{
  // wrapper constructors supporting StreamType() constructors
  foo& write(char const* s, std::streamsize count)
  {
    //...
    return *this;
  }
};

Usage:

foo<fstream> obj;
obj.write(...);

The usual solution for this sort of problem is to use templates. There aren't that many functions in an std::istream or and std::ostream which need covering, and a good template member for << and >> should cover a lot of the cases. In most of the cases I've done this, I've only offerred should cover a lot of the cases. In most of the cases I've done this, I've only offerred << or >>`. (Generally speaking, I've not needed bidirectional streams.)

As for handling other types of streams, just use std::iostream instead of std::fstream . (In general, except when opening files, you shouldn't see the fstream part.)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM