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;
}
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?
To remove the last character from the string, we will use the seekp() method of the stringstream class.
For clearing the contents of a stringstream , using: m. str("");
You can't return a stream from a function by value, because that implies you'd have to copy the stream.
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.
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;
}
My suggestion:
Create the std::string
to be returned by giving it the size.
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;
}
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!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With