1

When I have this class:

class mystreambuf : public std::streambuf
{
public:
    mystreambuf(const std::streambuf& other) : std::streambuf(other)
    {
    }
};

That compiles but when I try to do this nothing prints out:

mystreambuf buf(*std::cout.rdbuf());
std::cout.rdbuf(&buf);

std::cout << "Hello";

If buf is a copy of cout's buffer, why then does it not print anything?

Me myself and I
  • 3,990
  • 1
  • 23
  • 47
  • Doesn't compile for me. –  Jan 15 '14 at 21:06
  • @remyabel Try it again (but on Clang). GCC implements a private copy-constructor for `streambuf`. – Me myself and I Jan 15 '14 at 21:06
  • http://coliru.stacked-crooked.com/a/9ea1d8bec3751564 Nada on clang. But that's coliru, I don't have a personal copy of clang. –  Jan 15 '14 at 21:07
  • [You're not passing the right reference, read up this to educate yourself on which one to use.](http://www.cplusplus.com/articles/z6vU7k9E/) – Mike Jan 15 '14 at 21:07
  • @Mike It compiles on Clang 3.4. – Me myself and I Jan 15 '14 at 21:09
  • Has nothing to do with compiling, it's not going to cout his buffer because it's not pointing to it. – Mike Jan 15 '14 at 21:09
  • @Mike I know that. I was on a tangent for a second there. – Me myself and I Jan 15 '14 at 21:10
  • @Mike It is difficult to give a proper answer without being able to actually compile it. –  Jan 15 '14 at 21:10
  • @remyabel If you read 27.6.3.1 `[streambuf.cons]` you'll see that `std::streambuf`'s copy-constructor is protected, not private. I guess clang++ and gcc haven't caught up to that yet. But yet it compiles on Clang 3.4 for me... – Me myself and I Jan 15 '14 at 21:12
  • @Me No I found a website that does indeed compile your sample. It appears coliru uses gcc's implementation, which has a private copy constructor. –  Jan 15 '14 at 21:13
  • Try `mystreambuf* buf = (mystreambuf*)std::cout.rdbuf(); std::cout.rdbuf(buf);` –  Jan 15 '14 at 21:43
  • Note that using this method, there's no need to call the base copy constructor. http://coliru.stacked-crooked.com/a/82b7599f7b9c8cd4 However I cannot explain why this works, ergo why I deleted my answer. –  Jan 15 '14 at 21:54
  • @remyabel Why do I not need to call the base copy constructor? – Me myself and I Jan 15 '14 at 21:58
  • @remyabel What I'm trying to do is create a stream buffer with all the characteristics of `std::cout`'s stream buffer, but with extra member functions built in (for my use). Would you know how to do that? – Me myself and I Jan 15 '14 at 22:09

1 Answers1

4

There's nothing in the specification for stream buffers that says if you copy a stream buffer this way then the copy will continue to read/write from the same device, nor is cout specified such that its result from rdbuf() will behave as you desire.

streambuf is essentially an interface, and its copy constructor does not somehow virtually invoke the copy constructor of any class implementing the interface. Polymorphic types can't be held, copied, passed, etc. by value. In order to copy a polymorphic type you have to use something like a virtual clone() member function, and the streambuf interface does not include any such method.

In order to copy the streambuf used by cout you would have to access it using its dynamic type, and even then it might not be copiable: For example, it might be that multiple stream buffers accessing the same device would need to coordinate or synchronize in some fashion, and a streambuf implementation may disallow copying so that it can safely avoid having to do that synchronization.


What I'm trying to do is create a stream buffer with all the characteristics of std::cout's stream buffer, but with extra member functions built in (for my use). Would you know how to do that?

What extra characteristics are you trying to add? It may be that implementing streambuf is not the appropriate place to add your functionality.


If you want an implementation of the streambuf interface that forwards to a preexisting streambuf then you can create a streambuf class which holds a streambuf pointer, properly treating it as a pointer to a polymorphic type, and implementing the streambuf interface by forwarding calls to the internal streambuf. This is a really simple implementation:

#include <iostream>

struct copiable_streambuf : std::streambuf {
    std::streambuf *buf; // non-owning pointer

    copiable_streambuf(std::streambuf *buf) : buf(buf) {}

    std::streambuf::int_type overflow(std::streambuf::int_type c) {
        buf->sputc(c);
        return 0;
    }
};

int main()
{
    copiable_streambuf buf (std::cout.rdbuf());
    std::ostream os(&buf);
    os << "Hello, World!\n";
}

Your example code involves replacing the buffer used by cout. If you do this you should be sure to put the original buffer back before cout is destroyed at the end of the program, because cout manages the lifetime of its own stream buffer and when couts destructor runs it may require the stream buffer it's holding to be the one it originally created.

// RAII type for managing buffer switches.
struct buffer_switcher {
    std::ostream &os;
    std::streambuf *old;

    buffer_switcher(std::ostream &os, std::streambuf *buf)
        : os(os), old(os.rdbuf())
    {
        os.rdbuf(buf);
    }

    ~buffer_switcher() { os.rdbuf(old); }
};

int main() {
    // create our forwarding buffer
    copiable_streambuf buf(std::cout.rdbuf());

    // set up cout to use our buffer and simultaneously ensure that the buffer will get switched back as required
    buffer_switcher _(std::cout, &buf);

    std::cout << "Hello";    
}
bames53
  • 86,085
  • 15
  • 179
  • 244
  • Why did you implement `overflow` and in that way? – David G Jan 15 '14 at 23:09
  • @0x499602D2 If you slog through the specification of streambuf you'll find that, in the default implementation, all the output operations boil down to calling overflow, so you can create a minimal implementation for output just by overriding that one function. I implemented it so that it just forwards to another streambuf, thus writing to the same device as the original streambuf from `cout`. – bames53 Jan 15 '14 at 23:18
  • Actually, this answer is just what I need to answer another question that's been picking at me. Do you mind if I use the code? – David G Jan 15 '14 at 23:22
  • I see. I thought you would have to implement `sputc` and most other output functions as well to get it to work. – David G Jan 15 '14 at 23:29
  • "What extra characteristics are you trying to add?" - Nothing important, I'm just trying to be able to do: `out.rdbuf()->my_function()` where `my_function` is defined inside `mystreambuf`. – Me myself and I Jan 16 '14 at 00:06