Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get characters out of stringstream without copy?

What is the proper c++11 way to extract a set of characters out of a stringstream without using boost?

I want to do it without copying, if possible, because where this is used is in a critical data loop. It seems, though, std::string does not allow direct access to the data.

For example, the code below performs a substring copy out of a stringstream:

inline std::string left(std::stringstream ss, uint32_t count) {
    char* buffer = new char[count];
    ss.get(buffer, count);
    std::string str(buffer);  // Second copy performed here
    delete buffer;
    return str;
}
  1. Should I even be using char *buffer according to c++11?
  2. How do I get around making a second copy?

My understanding is that vectors initialize every character, so I want to avoid that.

Also, this needs to be passed into a function which accepts const char *, so now after this runs I am forced to do a .c_str(). Does this also make a copy?

It would be nice to be able to pass back a const char *, but that seems to go against the "proper" c++11 style.

To understand what I am trying to do, here is "effectively" what I want to use it for:

fprintf( stderr, "Data: [%s]...", left(ststream, 255) );

But the c++11 forces:

fprintf( stderr, "Data: [%s]...", left(str_data, 255).c_str() );

How many copies of that string am I making here?

How can I reduce it to only a single copy out of the stringstream?

like image 859
user3072517 Avatar asked Feb 22 '15 20:02

user3072517


People also ask

How do I delete a character from Stringstream?

To remove the last character from the string, we will use the seekp() method of the stringstream class.

How do you clean Stringstream?

For clearing the contents of a stringstream , using: m. str("");

Can you return a Stringstream?

You can't return a stream from a function by value, because that implies you'd have to copy the stream.

Is Stringstream slow C++?

string stream is slow. Quite very slow. If you are writing anything performance critical that acts on large data sets ( say loading assets after a level change during a game ) do not use string streams.


3 Answers

You could use something like described in this link: How to create a std::string directly from a char* array without copying?

Basically, create a string, call the resize() method on the string with the size that is passed to your function and then pass the pointer to the first character of the string to the stringstring.get() method. You will end up with only one copy.

inline std::string left(std::stringstream& ss, uint32_t count) {
    std::string str;
    str.resize(count);
    ss.get(&str[0], count);
    return str;
}
like image 139
fvannee Avatar answered Oct 12 '22 22:10

fvannee


My suggestion:

  1. Create the std::string to be returned by giving it the size.

  2. Read the characters one by one from the stringstream and set the values in the std::string.

Here's what the function looks like:

inline std::string left(std::stringstream ss, uint32_t count) {
    std::string str(count+1, '\0');
    for (uint32_t i = 0; i < count; ++i )
    {
        int c = ss.getc();
        if ( c != EOF )
        {
           str[i] = c;
        }
        else
        {
           break;
        }
    }
    return str;
}
like image 30
R Sahu Avatar answered Oct 12 '22 22:10

R Sahu


R Sahu, this I like! Obvious now that I see it done. ;-)

I do have one mod though (as well as passed a shared_ptr of stream which is what I actually had in my version):

In your initializer, you are filling with nulls. You only need to fill with the last one, so I propose a tweak of this:

inline std::string left(std::shared_ptr<std::stringstream> ss, uint32_t count) {
    std::string str;
    str.reserve(count + 1);
    uint32_t i;
    for(i = 0; i < count; ++i) {
        int c = ss->get();
        if(c != EOF) {
            str[i] = c;
        } else {
            break;
        }
    }
    str[i] = '\0';
    return str;
}

Now, only initialized with nulls on a single character.

Thanks R Sahu!

like image 29
user3072517 Avatar answered Oct 12 '22 23:10

user3072517