tags:

views:

754

answers:

4

Hello, this should be pretty common yet I find it fascinating that I couldn't find any straight forward solution.

Basically I read in a file over the network into a stringstream. This is the declaration:

std::stringstream membuf(std::ios::in | std::ios::out | std::ios::binary);

Now I have some C library that wants direct access to the read chunk of the memory. How do I get that? Read only access is OK. After the C function is done, I dispose of the memorystream, no need for it.

str() copies the buffer, which seems unnecessary and doubles the memory.

Am I missing something obvious? Maybe a different stl class would work better.

Edit: Apparently, stringstream is not guaranteed to be stored continuously. What is?

if I use vector<char> how do I get byte buffer?

+2  A: 

You can call str() to get back a std::string. From there you can call c_str() on the std::string to get a char*. Note that c_str() isn't offically supported for this use, but everyone uses it this way :)

Edit

This is probably a better solution: http://www.cplusplus.com/reference/iostream/istream/read/. From the example on that page:

  buffer = new char [length];

  // read data as a block:
  is.read (buffer,length);
Kris
Man, thats tripling the buffer.
Kugel
This however works. I will mark it as an answer if there is no better way in STL.
Kugel
`c_str()` is part of the standard interface of `std::basic_string`. Apart from the minor detail that it returns a `const char*` (and the question on asks for read-only access anyway), how is this usage of `c_str` not supported?
Charles Bailey
When you say "everyone uses it this way", I think you used the wrong word - you meant "no-one".
anon
regarding edit, I don't know the length beforehand to allocate the buffer.
Kugel
Actually I can count length by summing the incoming packets during streaming from network.
Kugel
@Kugel: Doesn't this imply that you have to store all the packets somewhere before sending them to the `stringstream`? In this case wouldn't you be better of just creating one big buffer and copying the data directly from the stored packets rather than using a `stringstream` intermediate?
Charles Bailey
@Charles - Your right. I thought that because it was the internal representation of the string you aren't suppose to use it. But I checked the SGI docs (http://www.sgi.com/tech/stl/basic_string.html) and it doesn't say anything like that.@Neil - "no-one" must include me and many other people I've worked with in the past. Using const char* is bad form in C++, but a lot of C++ code either has to interface with C libraries or was written by someone who knew C and was trying to code in C++. This seemed to be extremely common to me.
Kris
+3  A: 

std::stringstream doesn't (necessarily) store its buffer contiguously but can allocate chunks as it is gradually filled. If you then want all of its data in a contiguous region of memory then you will need to copy it and that is what str() does for you.

Of course, if you want to use or write a class with a different storage strategy then you can, but you don't then need to use std::stringstream at all.

Charles Bailey
Thanks for the continuity remark.
Kugel
Charles, can you help me? I know that I can get the underlying stringbuf used by the stringstream with 'rdbuf()'. And I cannot find wording in the Standard saying that the stringbuf/stringstream can be non-contiguous. I'd appreciate any pointers you can give. Thanks.
Don Wakefield
Where does it say that `stringbuf` has to use contiguous storage? The standard requires it to store an underlying character sequence but doesn't specify how. The `streambuf` interface has `overflow` and `underflow` so doesn't require the stream to be made available as a single contiguous range and the only other requirements are the constructor from `std::string` and the `str` overloads all of which deal with copies of the underlying character sequence. I'd be (mildly) disappointed with any implementation that always used contiguous storage as the interface is designed for incremental appends.
Charles Bailey
+3  A: 

Well, if you are seriously concerned about storage, you can get closer to the metal. *basic_stringstream* has a method, rdbuf() which returns it's *basic_stringbuf* (which is derived from *basic_streambuf*). You can then use the eback(), egptr(), and gptr() pointers to access characters directly out of the buffer. I've used this machinery in the past to implement a custom buffer with my desired semantics, so it is do-able.

Beware, this is not for the faint of heart! Set aside a few days, read Standard C++ IOStreams and Locales, or similar nitpicky reference, and be careful...

Don Wakefield
Thanks for the heads up I do know have time budget to dig in. I may as well implement my own memorystream.
Kugel
`eback`, `egptr` and `gptr` are protected so obtaining the `rdbuf` pointer for the `stringbuf` isn't going to give you access to these. Even if it did, I don't think that it's _guaranteed_ that you could create a contiguous copy of the underlying sequence with fewer copies that `.str()` and `.data()`.
Charles Bailey
Okay. Since I was implementing my own derived buffer, I could access the methods. And I guess G++ just does contiguous access. I still couldn't find this restriction (non-contiguous) in the standard...
Don Wakefield
A: 

You can take full control of the buffer used by writing the buffer yourself and using that buffer in the stringstream

stringstream membuf(std::ios::in | std::ios::out | std::ios::binary);
membuf.rdbuf(yourVeryOwnStreamBuf);

Your own buffer should be derived from basic_streambuf, and override the sync() and overflow() methods appropriately.

For your internal representation you could probably use something like vector< char >, and reserve() it to the needed size so that no reallocations and copies are done.

This implies you know an upper bound for the space needed in advance. But if you don't know the size in advance, and need a continguous buffer in the end, copies are of course unavoidable.

Pieter