0

Here is a simple piece of code what should print a std::ostringstream buffer, which in turn has been obtained via rdbuf(). I expect that this buffer to be printed to std::cout either via istreambuf iterators or directly via << operator. Surprisingly nothing can print this buffer. The more interesting that << operator clears this buffer out.

#include <iostream>
#include <sstream>
#include <algorithm>
#include <iterator>

using namespace std;

int main()
{
    std::ostringstream buf_ref;
    buf_ref << "bgdgdgdfg" << std::endl;

    auto buff = buf_ref.rdbuf();


    std::cout << "1 " << buf_ref.str() << std::endl;

    std::copy(std::istreambuf_iterator<char>(buff), std::istreambuf_iterator<char>(), std::ostream_iterator<char>(std::cout));

    std::cout << "2 " << buf_ref.str() << std::endl;

    std::cout << buff << std::endl; /*Why it clears the buffer? Why it doesn't output anything?*/

    std::cout << "3 " << buf_ref.str() << std::endl;

    return 0;
}

Any ideas what is wrong here? BTS, Here is the reference which describes that << operator is overloaded for a streambuf* cplusplus.com/reference/ostream/ostream/operator%3C%3C So it should be a content of the streambuf to be printed.

Sergey
  • 11
  • 5
  • I am not exactly sure about most of this, but `buff` should be a pointer to `std::basic_stringbuf`, so this should actually output an address. – n314159 Nov 14 '19 at 11:29
  • The `buff` variable takes on the `std::stringbuf*` type so, as @n314159 says, you would *expect* a pointer value to be written. Maybe there's a specific overload for stringstream/stringbuffer pointers. Anyway, this will work: `std::cout << buff->str() << std::endl;` – Adrian Mole Nov 14 '19 at 12:00
  • No, here is the reference which describes that << operator is overloaded for a streambuf* http://www.cplusplus.com/reference/ostream/ostream/operator%3C%3C/ So it should be a content of the streambuf – Sergey Nov 14 '19 at 13:39

2 Answers2

0

The more interesting that << operator clears this buffer out.

Well, << reads data from the stream unitl the stream gets empty, by default it reads chunk of data to the first whitespace.

Surprisingly nothing can print this buffer.

The easiest is to take buff.str() to print out the content. In this way you get a copy of the data, so it will be not removed from the original stream.

I expect that this buffer to be printed to std::cout

Maybe this small example will throw some light on the mechanism how it works.

stringstream buf;
streambuf *backup;

backup = cout.rdbuf();
cout.rdbuf(buf.rdbuf());
buf << "normal buf" << endl;
cout << "cout to buf" << endl;
cout.rdbuf(backup);
cout << "normal cout" << endl;
cout << buf.str() << endl;

which leads to the following output:

normal cout
normal buf
cout to buf

but probably you mean the functionality similar to this:

stringstream buf;
streambuf *ptr;
ptr = buf.rdbuf();
// cout << buf.rdbuf(); // if you do it, check error state flags
// cout.clear(); // and/or clear it
buf << 'A' << endl;
cout << buf.rdbuf();
buf << 'B' << endl;
cout << ptr;

which print out:

A
B
Stefan
  • 139
  • 10
  • `ostream::rdbuf(streambuf*)` functions actually replace the receiving stream's buffer. Don't do that! – Tanveer Badar Nov 14 '19 at 12:16
  • you can also read from this buffer(`stringstream`) like from `cin`, e. g. int by int, char by char without whitespaces, etc. But, if you use `ostringstream` then you can use it only like the `cout`. – Stefan Nov 14 '19 at 12:17
  • Why? I have made a backup pointer and after job done I bring back the `cout` to the original. Please elaborate more, why not? You can do it with any other stream. – Stefan Nov 14 '19 at 12:21
  • This, while correct as an answer, makes two different streams write to the same buffer. I fear someone will come along in a couple of months and copy the code off of this and use in their application and wonder why they are seeing data corruption on output. – Tanveer Badar Nov 14 '19 at 12:53
  • Stefan, you correct about your second variant, I want do the same in my piece of code. I see that your 2nd variant does absolutely the same as mine. It takes streambuf from a string stream and prints it out to the cout. But notice that you use stringstream and I used ostringstream. I got a clue, thanks! – Sergey Nov 14 '19 at 13:56
  • So the other question is how to convert the output buffer into input buffer, since they have a difference? – Sergey Nov 14 '19 at 13:59
  • https://stackoverflow.com/questions/19687570/tricks-to-pass-from-a-ostringstream-to-a-istringstream – Stefan Nov 14 '19 at 20:47
0

If your goal is to print the stream buffer of an output stringstream through its rdbuf() member, then that will not work. You can't convert an output stringstream buffer into an input stringstream buffer because the openmode is set at construction, and there's no way to change that. If a stream was opened for output (on a stringstream) then it cannot be used for input, and vice versa. You will have to copy everything over into an std::istringstream or std::stringbuf.

std::ostringstream oss("abc");

std::istringstream iss(oss.str()); // 1
std::stringbuf buffer(oss.str()); // 2

std::cout << iss.rdbuf(); // 1
std::cout << &buffer; // 2

Or just use a std::stringstream.

David G
  • 94,763
  • 41
  • 167
  • 253