views:

968

answers:

3

I've been googling around and I just can't find a simple answer to this. And it should be simple, as the STL generally is.

I want to define MyOStream which inherits publicly from std::ostream. Let's say I want to call foo() each time something is written into my stream.

class MyOStream : public ostream {
public:
  ...
private:
   void foo() { ... }
}

I understand that the public interface of ostream is non-virtual, so how can it be done? I want clients to be able to use both operator<< and write() and put() on MyOStream and have use the extended ability of my class.

A: 

Composition, not inheritance. Your class contains, "wraps" an ostream&, and forwards to it (after calling foo()).

tpdi
Please post some code that illustrates how this would work with the existing << operators. And note foo() is to be called each time such an operator is used.
anon
Composition is not always the best solution, just as inheritance isn't. ostream has a dozen of overloaded operators implemented for it, you don't expect anyone to really rewrite all the public interface of ostream just to add a small functionality to a class.
Michael
+2  A: 

It's not a simple question, unfortuunately. The classes you should derive from are the basic_ classes, such as basic_ostream. However, derivation from a stream may not be what you want, you may want to derive from astream buffer instead, and then use this class to instantiate an existing stream class.

The whole area is complex, but there is an excellent bokk about it Standard C++ IOStreams and Locales, which I suggest you take a look at before going any further.

anon
I was going to go look up my copy of this book, but you've saved me having to. +1
Evan
A: 

I don't know if this is correct solution, but I inherited it this way. It uses buffer and gets 64 bytes (or less if flushed) and sends them to generic putChars() method where the actual handling of data is done. It also demonstrates how to give user data.

I haven't checked if this compiles or not, but I think you get the point.

class MyBuffer : public std::basic_streambuf< char, std::char_traits< char > >
{

public:

    inline MyBuffer(MyData data) :
    data(data)
    {
        setp(buf, buf + BUF_SIZE);
    }

protected:

    // This is called when buffer becomes full. If
    // buffer is not used, then this is called every
    // time when characters are put to stream.
    inline virtual int overflow(int c = Traits::eof())
    {
        // Handle output
        putChars(pbase(), pptr());
        if (c != Traits::eof()) {
            char c2 = c;
            // Handle the one character that didn't fit to buffer
            putChars(&c2, &c2 + 1);
        }
        // This tells that buffer is empty again
        setp(buf, buf + BUF_SIZE);
        // I'm not sure about this return value!
        return 0;
    }

    // This function is called when stream is flushed,
    // for example when std::endl is put to stream.
    inline virtual int sync(void)
    {
        // Handle output
        putChars(pbase(), pptr());
        // This tells that buffer is empty again
        setp(buf, buf + BUF_SIZE);
        return 0;
    }

private:

    // For EOF detection
    typedef std::char_traits< char > Traits;

    // Work in buffer mode. It is also possible to work without buffer.
    static size_t const BUF_SIZE = 64;
    char buf[BUF_SIZE];

    // This is example about userdata
    MyData data;

    // In this function, the characters are parsed.
    void putChars(char const* begin, char const* end);

};

class MyOStream : public std::basic_ostream< char, std::char_traits< char > >
{

public:

    inline MyOStream(MyData data) :
    std::basic_ostream< char, std::char_traits< char > >(&buf),
    buf(data)
    {
    }

private:

    MyBuffer buf;

};
Henrik Heino