12

So istringstream copies the contents of a string when initialised, e.g

string moo("one two three four");
istringstream iss(moo.c_str());

I was wondering if there's a way to make std::istringstream use the given c_str as its buffer without copying things. This way, it won't have to copy large bits of memory before passing the std::istringstream& to functions that take istream& as an argument.

What I've been trying to do is convert some functions which only take std::ifstream& arguments (they're mostly parsers) into taking istream& as well. Would I have to make my own istream subclass for this?

Null
  • 1,940
  • 9
  • 24
  • 29
kamziro
  • 7,112
  • 7
  • 51
  • 74
  • See [this answer](http://stackoverflow.com/questions/2786816/how-to-create-c-istringstream-from-a-char-array-with-null0-characters/2786872#2786872) for a way to do it (basically identical to @Charles's solution, but wrapping a `istream` around it for convenience). – Johannes Schaub - litb Jun 26 '10 at 11:25

5 Answers5

7

Using istringstream is not a satisfactory solution, because this copies the entire buffer.

A previous answer suggests the deprecated istrstream, but as this generates warnings and may be removed in future, a better solution is to use boost::iostreams:

boost::iostreams::stream<boost::iostreams::array_source> stream(moo.c_str(), moo.size());

This avoids copying the buffer in the same way istrstream did, and saves you having to write your own stream class.

Riot
  • 13,698
  • 3
  • 55
  • 59
5

It's fairly trivial to write a basic std::streambuf class that reads from a given memory area. You can then construct an istream from this and read from that.

initializing a C++ std::istringstream from an in memory buffer?

Note that the lifetime of the buffer pointed to be c_str() is very limited, though, and there's no guarantee that a call to c_str() want cause some copying although I don't know of any implementations where it does.

Community
  • 1
  • 1
CB Bailey
  • 648,528
  • 94
  • 608
  • 638
  • Ah, thanks for that, will give that a try. It looks a lot simpler than what I imagined it would be :) – kamziro Jun 27 '10 at 10:41
  • In my case this definitely doesn't work the same as `istringstream`, solution by @Riot bellow does work though as expected. In my case code updates get pointer in the istringstream while reading using `seekg`. In other words proper solution would require seekpos overload as well to match behavior of `ostreingstream` – Pavel P Jan 27 '19 at 06:50
3

the deprecated istrstream supports this feature.

#include <string>
#include <strstream>
using namespace std;

int main(int argc, char* argv[])
{
    string moo = "one two three four";
    istrstream istr(const_cast<char*>(moo.c_str()),moo.size());
    std::string line;
    while(!istr.fail() && !istr.eof()){
        getline(istr,line,' ');
        cout << line << "_";
    }
    // prints: one_two_three_four_
}
smerlin
  • 5,998
  • 3
  • 31
  • 54
  • @sbi: my bad, i did some tests with strstream first, was a leftover from that, well its fixed now. – smerlin Jun 26 '10 at 09:20
2

There's only a copy because the parameter you pass, a const char*, requires conversion to the argument type of the istringstream constructor.

Just pass in the string without calling c_str().

istringstream iss(moo);

Well ok, that doesn't prevent copying completely, but it eliminates an unnecessary copy. To completely eliminate the copy, you'd have to rewrite std::stringbuf, which specifically avoids working directly on the string you give it.

Ben Voigt
  • 260,885
  • 36
  • 380
  • 671
2

It depends on what a std::string does. According to 27.2.1/1 The class basic_istringstream<charT,traits,Allocator> ... uses a basic_stringbuf<charT,traits,Allocator> object to control the associated storage. Since the class must use an object it must copy construct the string into that object.

So the real question is not whether a stringstream copies the contents, but whether copy constructing a string will copy the contents or implement some sort of copy-on-write scheme.

R Samuel Klatchko
  • 70,693
  • 15
  • 126
  • 182