Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Move the string out of a std::ostringstream

If I construct a string made of a list of space separated floating point values using std::ostringstream:

std::ostringstream ss; unsigned int s = floatData.size(); for(unsigned int i=0;i<s;i++) {     ss << floatData[i] << " "; } 

Then I get the result in a std::string:

std::string textValues(ss.str()); 

However, this will cause an unnecessary deep copy of the string contents, as ss will not be used anymore.

Is there any way to construct the string without copying the entire content?

like image 686
galinette Avatar asked Oct 08 '14 21:10

galinette


2 Answers

std::ostringstream offers no public interface to access its in-memory buffer unless it non-portably supports pubsetbuf (but even then your buffer is fixed-size, see cppreference example)

If you want to torture some string streams, you could access the buffer using the protected interface:

#include <iostream> #include <sstream> #include <vector>  struct my_stringbuf : std::stringbuf {     const char* my_str() const { return pbase(); } // pptr might be useful too };  int main() {     std::vector<float> v = {1.1, -3.4, 1/7.0};     my_stringbuf buf;     std::ostream ss(&buf);     for(unsigned int i=0; i < v.size(); ++i)         ss << v[i] << ' ';     ss << std::ends;     std::cout << buf.my_str() << '\n'; } 

The standard C++ way of directly accessing an auto-resizing output stream buffer is offered by std::ostrstream, deprecated in C++98, but still standard C++14 and counting.

#include <iostream> #include <strstream> #include <vector>  int main() {     std::vector<float> v = {1.1, -3.4, 1/7.0};     std::ostrstream ss;     for(unsigned int i=0; i < v.size(); ++i)         ss << v[i] << ' ';     ss << std::ends;     const char* buffer = ss.str(); // direct access!     std::cout << buffer << '\n';     ss.freeze(false); // abomination } 

However, I think the cleanest (and the fastest) solution is boost.karma

#include <iostream> #include <string> #include <vector> #include <boost/spirit/include/karma.hpp> namespace karma = boost::spirit::karma; int main() {     std::vector<float> v = {1.1, -3.4, 1/7.0};     std::string s;     karma::generate(back_inserter(s), karma::double_ % ' ', v);     std::cout << s << '\n'; // here's your string } 
like image 195
Cubbi Avatar answered Oct 02 '22 14:10

Cubbi


This is now possible with C++20, with syntax like:

const std::string s = std::move(ss).str(); 

This is possible because the std::ostringstream class now has a str() overload that is rvalue-ref qualified:

basic_string<charT, traits, Allocator> str() &&;  // since C++20 

This was added in P0408, revision 7, which was adopted into C++20.

like image 33
NicholasM Avatar answered Oct 02 '22 13:10

NicholasM