20

I use std::stringstream extensively to generate code. I have run into some querious behavior when setting the content with the str() function followed by using the operator<<. Can someone explain this behavior to me. I would very much appreciate it - Thanks.

Example One:

std::stringstream ssa;
ssa.str("Setting string");
std::cout << ssa.str() << std::endl;
ssa << " adding to string";
std::cout << ssa.str() << std::endl;

Output:

Setting string

adding to string

Expected:

Setting string

Setting string adding to string

So after a bit of reading up on the documentation I found that I should set the open mode to ios_base::ate:

std::stringstream ssb(std::ios_base::ate);
ssb.str("Setting string");
std::cout << ssb.str() << std::endl;
ssb << " adding to string";
std::cout << ssb.str() << std::endl;

Output:

Setting string

Setting string

Again (or now) I would expect:

Setting string

Setting string adding to string

Why am I getting the results I am, and how do I get the result I want? E.i. Being able to set the initial string of a stringstream and the append to that string ?

I am aware that I can just set the content to the empty string("") and then only use the <<operator. This is in fact the workaround I am using. I just don't understand the observed behavior. And the workaround makes my code more messy.

Community
  • 1
  • 1
Troels Blum
  • 823
  • 8
  • 17
  • 1
    For the second one, either do `std::ios_base::out | std::ios_base::ate` or use a `ostringstream`. – T.C. Jun 17 '15 at 18:36

2 Answers2

12

The default open mode for a std::stringstream object is in | out. Using a different mode overrides these options. Since the stream cannot be written to, the insertion operator cannot be used to write to the stream. This is why str() gives the same output twice.

You need to manually open the stream in output mode as well:

std::stringstream ssb("Setting string", std::ios_base::out | std::ios_base::ate);

Live Demo

Alternatively, use an std::ostringstream, which initializes the internal std::stringbuf subobject with out | mode.

std::ostringstream("Setting string", std::ios_base::ate);

Live Demo

David G
  • 94,763
  • 41
  • 167
  • 253
  • just out of curiosity, what's the point of having `streamstream` w/o `in` and `out` modes? Ok, I can imagine they can be added later. But why output operator doesn't throw an exception if `streamstream` doesn't have `out` mode? Ignoring silently doesn't look good – Andriy Tylychko Jun 24 '15 at 15:24
  • @AndyT Concrete `std::iostream` classes (`std::fstream` and `std::stringstream`) are built assuming you know how to initialize them. So it passes the mode to the underlying buffer as is, because it can't assume you want anything specific. I can't think of any use case for a stream that can't perform input or output. Also, streams don't throw exceptions by default but you may turn on exceptions using the `std::basic_ios::exceptions()` mask. – David G Jun 24 '15 at 15:47
8

You forgot to set flag mode: std::ios_base::out. This will work:

std::stringstream ssb(std::ios_base::out | std::ios_base::ate);
ssb.str("Setting string");
std::cout << ssb.str() << std::endl;
ssb << " adding to string";
std::cout << ssb.str() << std::endl;

Output:

Setting string
Setting string adding to string
Andreas DM
  • 10,685
  • 6
  • 35
  • 62
  • 1
    This is why I prefer using `std::istreamstream` and `std::ostringstream` instead of `std::stringstream`, then there is no confusion about the input/output mode. – Remy Lebeau Jun 17 '15 at 18:41
  • Does reading from `str()` close and reopen the stream? Is that why `std::ios_base::ate` is needed to make this work? Without it, `<< " adding to string"` overwrites, rather than appends to, the data set by the previous `str("Setting string")`. – Remy Lebeau Jun 17 '15 at 18:45
  • @RemyLebeau yes, added to the answer, it does overwrite without ios_base::ate – Andreas DM Jun 17 '15 at 18:54
  • But **why** does it overwrite? `ate` is applied when a stream is *opened* to seek to the end of existing content. So is `cout << ssb.str()` or `ssb << " adding ..."` re-opening the stream? But then wouldn't the existing content get wiped? That is what I don't understand. From all indications, reading `str()` just makes a copy of the current `rdbuf` content, so why is an extra seek-to-end needed afterwards? I cannot find any documentation on that. – Remy Lebeau Jun 17 '15 at 18:59
  • 1
    @RemyLebeau You can't open or close a stringstream. The `str()` method reinitializes the buffer by resetting the internal pointers. In particular, the *current pointer* is set to the beginning of the new buffer, which is why it is being overwritten by the next insertion. What `ate` does is keep the *current pointer* at the end of the buffer, so that each insertion appends to the stream. – David G Jun 17 '15 at 19:05
  • Thank you. Would be nice if the documentation explained that. – Remy Lebeau Jun 17 '15 at 19:08
  • @RemyLebeau It can be found in the documentation for [`std::stringbuf::str()`](http://en.cppreference.com/w/cpp/io/basic_stringbuf/str) – David G Jun 17 '15 at 19:18
  • I already looked at that page, but do not see it say that reading from `str()` resets the buffer unless `ate` is used. But now I do see that it says how writing to `str()` initializes the pointers for subsequent reads/writes, and that `ate` allows subsequent output to be appended to the existing characters from a previous write to `str()`, but only in C++11 and later. – Remy Lebeau Jun 17 '15 at 19:46
  • @RemyLebeau I didn't mean that calling the zero-argument overload of `str()` resets the buffer pointers, but the `str(basic_string)` overload does. Also, see [LWG Issue 1448](http://www.open-std.org/JTC1/SC22/WG21/docs/lwg-defects.html#1448). Until C++11 it was implementation-defined what would happen when using `ate`. – David G Jun 17 '15 at 20:39