C ++ Decorate basic_iostream classes

I want to do something like the following code:

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 (e.g. put ). This should not apply only to file streams, but all other stream classes. Is there an easy way to enable these features?

+4
source share
5 answers

Many of the formatted output operator<< ( operator<< ) are written directly to the stream buffer. What you need to do to accomplish this in a general way is to get a class from std :: basic_streambuf, which forwards all the data to another std :: basic_streambuf, and then additionally creates a minimal implementation of std :: basic_ostream to make it easier to use your stream buffer.

I would not say that it is especially simple, but this is the only way to do it in a way that can affect all types of streams.

Here is an example of a minimal stream buffer that sends to another stream buffer (and performs some meaningless conversions 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; } 

Gpp will be Gpp . Hope this helps you along the way.

+5
source

Something like that?

  template <class Stream> class DecoratedStream { public: DecoratedStream(Stream* stream) : m_stream(stream) {} DecoratedStream& write(const char* data, int count) { m_stream->write(data, count); } }; 
+2
source

If I understand you correctly, you want to decorate the methods of any iostream . So just have your decorator accept iostream as decoratee (unlike fstream , which is a subclass of iostream ).

+2
source

Having a pointer inside the structure as your current approach is dangerous and error prone. Instead, simply output such stream classes and implement basic constructors and wrap around your own 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; } }; 

Using:

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

A common solution for this kind of task is to use templates. There are not many functions in std::istream or or 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 do not need bidirectional streams.)

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

0
source

All Articles