I have a std::string which could be a string or could be a value (such as 0).
What is the best or easiest way to convert the std::string to int with the ability to fail? I want a C++ version of C#'s Int32.TryParse.
Use boost::lexical_cast. If the cast cannot be done, it will throw an exception.
#include <boost/lexical_cast.hpp>
#include <iostream>
#include <string>
int main(void)
{
std::string s;
std::cin >> s;
try
{
int i = boost::lexical_cast<int>(s);
/* ... */
}
catch(...)
{
/* ... */
}
}
Without boost:
#include <iostream>
#include <sstream>
#include <string>
int main(void)
{
std::string s;
std::cin >> s;
try
{
std::stringstream ss(s);
int i;
if ((ss >> i).fail() || !(ss >> std::ws).eof())
{
throw std::bad_cast();
}
/* ... */
}
catch(...)
{
/* ... */
}
}
Faking boost:
#include <iostream>
#include <sstream>
#include <string>
template <typename T>
T lexical_cast(const std::string& s)
{
std::stringstream ss(s);
T result;
if ((ss >> result).fail() || !(ss >> std::ws).eof())
{
throw std::bad_cast();
}
return result;
}
int main(void)
{
std::string s;
std::cin >> s;
try
{
int i = lexical_cast<int>(s);
/* ... */
}
catch(...)
{
/* ... */
}
}
If you want no-throw versions of these functions, you'll have to catch the appropriate exceptions (I don't think boost::lexical_cast provides a no-throw version), something like this:
#include <iostream>
#include <sstream>
#include <string>
template <typename T>
T lexical_cast(const std::string& s)
{
std::stringstream ss(s);
T result;
if ((ss >> result).fail() || !(ss >> std::ws).eof())
{
throw std::bad_cast();
}
return result;
}
template <typename T>
bool lexical_cast(const std::string& s, T& t)
{
try
{
// code-reuse! you could wrap
// boost::lexical_cast up like
// this as well
t = lexical_cast<T>(s);
return true;
}
catch (const std::bad_cast& e)
{
return false;
}
}
int main(void)
{
std::string s;
std::cin >> s;
int i;
if (!lexical_cast(s, i))
{
std::cout << "Bad cast." << std::endl;
}
}
The other answers that use streams will succeed even if the string contains invalid characters after a valid number e.g. "123abc". I'm not familiar with boost, so can't comment on its behavior.
If you want to know if the string contains a number and only a number, you have to use strtol:
#include <iostream>
#include <string>
int main(void)
{
std::string s;
std::cin >> s;
char *end;
long i = strtol( s.c_str(), &end, 10 );
if ( *end == '\0' )
{
// Success
}
else
{
// Failure
}
}
strtol returns a pointer to the character that ended the parse, so you can easily check if the entire string was parsed.
Note that strtol returns a long not an int, but depending on your compiler these are probably the same. There is no strtoi function in the standard library, only atoi, which doesn't return the parse ending character.
Exceptions should not be used for boolean tests
The accepted answer is really a terrible answer for question as asked, as it violates the precept "use exceptions for exceptional cases".
Exceptions are an excellent tool for handling exceptional cases -- cases where something has genuinely gone wrong. They are poor tools for existing use-cases. Partly because throwing and catching an exception is expensive, and partly because it is misleading code -- when a developer sees an exception they should reasonably be able to assume something is going wrong there. Good discussions of this basic principle abound, but I like "The Pragmatic Programmer"'s, or this isn't bad: http://www.lohmy.de/2013/03/06/writing-use-cases-exception-or-alternate-flow/
Use boost::lexical_cast if you always expect a number
boost::lexical_cast is an optimal solution when it really is an exception for it to be receiving a non-number.
Use boost::try_lexical_convert if non-numbers are part of your use case
If you are going through a string and want to do one thing if it's a number, and another if it's a number, don't use an exception for the boolean test. That's just bad programming.
In fact, boost offers try_lexical_convert, which is used in the implementation of lexical_cast (taken from the documentation here:
http://www.boost.org/doc/libs/1_58_0/doc/html/boost_lexical_cast/synopsis.html#boost_lexical_cast.synopsis.lexical_cast).
template <typename Target, typename Source>
inline Target lexical_cast(const Source &arg)
{
Target result;
if (!conversion::try_lexical_convert(arg, result))
throw bad_lexical_cast();
return result;
}
Another way using standard streams :
#include <sstream>
#include <iostream>
#include <string>
int main()
{
std::stringstream convertor;
std::string numberString = "Not a number!";
int number;
convertor << numberString;
convertor >> number;
if(convertor.fail())
{
// numberString is not a number!
std::cout << "Not a Number!";
}
}
Before boost's lexical_cast was available, I used to do the following:
namespace detail {
template< typename Target, typename Source >
struct stream_caster {
static Target stream_cast(const Source& s)
{
std::stringstream ss;
if( (ss << s).fail() ) {
throw std::bad_cast("could not stream from source");
}
Target t;
if( (ss >> t).fail() || !(ss >> ws).eof()) {
throw std::bad_cast("could not stream to target");
}
return t;
}
};
template< typename T >
struct stream_caster<T,T> {
static const T& stream_cast(const T& s)
{
return s;
}
};
template< typename Source >
struct stream_caster<std::string,Source> {
static std::string stream_cast(const Source& s)
{
std::ostringstream oss;
if( (oss << s).fail() ) {
throw std::bad_cast("could not stream from source");
}
return oss.str();
}
};
template< typename Target >
struct stream_caster<Target,std::string> {
static Target stream_cast(const std::string& s)
{
std::stringstream ss(s);
Target t;
if( (ss >> t).fail() || !(ss >> ws).eof()) {
throw std::bad_cast("could not stream to target");
}
return t;
}
};
template<>
struct stream_caster<std::string,std::string> {
static const std::string& stream_cast(const std::string& s)
{
return s;
}
};
}
template< typename Target, typename Source >
inline Target stream_cast(const Source& s)
{
return detail::stream_caster<Target,Source>::stream_cast(s);
}
Related
Is there a safe standard way to convert std::string_view to int?
Since C++11 std::string lets us use stoi to convert to int:
std::string str = "12345";
int i1 = stoi(str); // Works, have i1 = 12345
int i2 = stoi(str.substr(1,2)); // Works, have i2 = 23
try {
int i3 = stoi(std::string("abc"));
}
catch(const std::exception& e) {
std::cout << e.what() << std::endl; // Correctly throws 'invalid stoi argument'
}
But stoi does not support std::string_view. So alternatively, we could use atoi, but one has to be very careful, e.g.:
std::string_view sv = "12345";
int i1 = atoi(sv.data()); // Works, have i1 = 12345
int i2 = atoi(sv.substr(1,2).data()); // Works, but wrong, have i2 = 2345, not 23
So atoi does not work either, since it is based off the null-terminator '\0' (and e.g. sv.substr cannot simply insert/add one).
Now, since C++17 there is also from_chars, but it does not seem to throw when providing poor inputs:
try {
int i3;
std::string_view sv = "abc";
std::from_chars(sv.data(), sv.data() + sv.size(), i3);
}
catch (const std::exception& e) {
std::cout << e.what() << std::endl; // Does not get called
}
The std::from_chars function does not throw, it only returns a value of type from_chars_result which is a struct with two fields:
struct from_chars_result {
const char* ptr;
std::errc ec;
};
You should inspect the values of ptr and ec when the function returns:
#include <iostream>
#include <string>
#include <charconv>
int main()
{
int i3;
std::string_view sv = "abc";
auto result = std::from_chars(sv.data(), sv.data() + sv.size(), i3);
if (result.ec == std::errc::invalid_argument) {
std::cout << "Could not convert.";
}
}
Unfortunately, there is no standard way that would throw an exception for you but std::from_chars has a return value code that you may use:
#include <charconv>
#include <stdexcept>
template <class T, class... Args>
void from_chars_throws(const char* first, const char* last, T &t, Args... args) {
std::from_chars_result res = std::from_chars(first, last, t, args... );
// These two exceptions reflect the behavior of std::stoi.
if (res.ec == std::errc::invalid_argument) {
throw std::invalid_argument{"invalid_argument"};
}
else if (res.ec == std::errc::result_out_of_range) {
throw std::out_of_range{"out_of_range"};
}
}
Obviously you can create svtoi, svtol from this, but the advantage of "extending" from_chars is that you only need a single templated function.
Building on #Ron and #Holt's excellent answers, here's a small wrapper around std::from_chars() that returns an optional (std::nullopt when the input fails to parse).
#include <charconv>
#include <optional>
#include <string_view>
std::optional<int> to_int(const std::string_view & input)
{
int out;
const std::from_chars_result result = std::from_chars(input.data(), input.data() + input.size(), out);
if(result.ec == std::errc::invalid_argument || result.ec == std::errc::result_out_of_range)
{
return std::nullopt;
}
return out;
}
Utilizing boost, I would like to
read options from an inifile, abort if an unknown option is encountered in the inifile and
save them later in another inifile.
The first part can be done with boost::program_options:
try{
inifile_options.add_options()
("ops1.i0", po::value<int>(&p.nx)->default_value(1), "test integer")
;
po::variables_map vm;
po::store(po::parse_config_file(pthfnini, inifile_options), vm);
po::notify(vm);
}
catch(exception& e){
cerr << "error: " << e.what() << "\n";
errorflag=1;
}
To the best of my knowledge writing an inifile is not possible with boost::program_options, but boost::property_tree works:
pt::ptree iniPropTree;
pt::ini_parser::write_ini("./used0.ini",iniPropTree);
Now the question is how can I translate the data stored in the po::variables_map to pt::ptree?
Reading the boost documentation leaves me with the impression that this is not possible. Is the following the only viable way?
iniPropTree.put<int>("ops1.i0",vm["ops1.i0"].as<int>();
It introduces quite a bit of redundancy for my taste. However, reading data into a property tree from the beginning does not seem to support checking for undefined/misspelled options.
Alternatively,is it possible to iterate over the contents of variables_map and somehow infer the corresponding datatype of each element?
The full code is here:
/*
* g++ iniOps_test.cpp -Wall -std=c++11 -O3 -lboost_system -lboost_program_options -o iniOps_test.exe
*
*/
// C++11 & Boost libraries
#include <boost/program_options.hpp> // po::options_description, po::variables_map, ...
#include <boost/property_tree/ptree.hpp> // pt::ptree
#include <boost/property_tree/ini_parser.hpp> // write_ini()
#include <iostream> // cout
#include <fstream> // ofstream, ifstream
// namespaces
namespace po = boost::program_options;
namespace pt = boost::property_tree;
using namespace std;
struct params{
std::string inipthfn;
int i0;
};
void read_inifile(params &p, po::variables_map &vm){
// initialize variables
int errorflag=0;
std::ifstream pthfnini("./testini.ini");
po::options_description inifile_options("Allowed inifile options");
try{
inifile_options.add_options()
("ops1.i0", po::value<int>(&p.i0)->default_value(1), "test integer")
;
;
po::store(po::parse_config_file(pthfnini, inifile_options), vm);
po::notify(vm);
}
catch(exception& e){
cerr << "error: " << e.what() << "\n";
errorflag=1;
}
pthfnini.close();
if(errorflag){ std::cout<<"--- program shutdown due to error in read_inifile ---"<<std::endl; exit(1); }
}
int main(){
params p;
po::variables_map vm;
pt::ptree iniPropTree;
read_inifile(p,vm); // get options from inifile
// ??? conversion from vm -> pt ???
pt::ini_parser::write_ini("./used0.ini",iniPropTree); // save options to used.ini
cout << p.i0 << endl;
return 0;
}
The contents of the inifile "testini.ini" are:
[ops1]
i0=2
There's a conceptual problem here.
Commandline parameters are inherently textual.
Values in the variables-map aren't. The types used are configured in the value-semantics (part of your options-description).
If all your options have the same type, you can "cheat" and hard-code a conversion:
pt::ptree to_ptree(po::variables_map const& vm) {
pt::ptree tree;
for (auto& v : vm) {
if (!v.second.empty() && !v.second.defaulted())
tree.put(v.first, v.second.as<int>());
}
return tree;
}
Which saves:
[ops1]
i0=1
If you need more flexibility, you'd need access to the option descriptions at the very least. This is not the intended use of the library, and you'll probably run into undocumented parts of the implementation soon.
However, reading data into a property tree from the beginning does not seem to support checking for undefined/misspelled options
Well. That's not entirely true. You can make your own parse function that adds the logic. Use Property Tree Translators if you want.
Here's an extended example showing three parameters of varying types to be validated:
enum class restricted { value1, value2 };
struct params {
int i0 = 1;
restricted r1 = restricted::value2;
std::string s2 = "some default";
};
We will want to have a parse function like this:
params read_inifile(std::string filename) {
params p;
pt::ptree tree;
std::ifstream file(filename);
read_ini(file, tree);
p.i0 = tree.get("ops1.i0", 1);
p.r1 = tree.get("ops1.r1", restricted::value2);
p.s2 = tree.get("ops1.s2", "some default");
return p;
}
Streamable Types
To translate and validate the enum you merely need to implement the streaming operators:
static inline std::istream& operator>>(std::istream& is, restricted& r) {
std::string v;
if (is >> std::ws >> v) {
if (boost::iequals("value1", v))
r = restricted::value1;
else if (boost::iequals("value2", v))
r = restricted::value2;
else
throw std::runtime_error("invalid restricted value");
}
return is;
}
static inline std::ostream& operator<<(std::ostream& os, restricted r) {
switch(r) {
case restricted::value1: return os << "value1";
case restricted::value2: return os << "value2";
default: return os << "invalid";
}
}
Custom Translators
Let's imagine that i0 need custom validation. In this example, let's REQUIRE it to be an odd number:
namespace translators {
template <typename T>
struct must_be_odd {
typedef T internal_type;
typedef T external_type;
boost::optional<T> get_value(const std::string& str) const {
if (str.empty()) return boost::none;
T v = boost::lexical_cast<T>(str);
if (v % 2 == 0)
throw std::runtime_error("value must be odd");
return boost::make_optional(v);
}
boost::optional<std::string> put_value(const T& i0) {
assert(i0 % 2); // assert that the value was odd
return boost::lexical_cast<std::string>(i0);
}
};
static const must_be_odd<int> i0;
}
Now we can simply supply the translator (here, acting more like a custom validator like Boost Program Options also has them):
p.i0 = tree.get("ops1.i0", 1, translators::i0);
See it Live On Coliru
Unsupported Options
This is a bit more work. You'll have to iterate the tree checking the resultant paths against a known set. Here's a stab at a reasonably generic implementation of that (which should work correctly with case sensitive trees of any (wide) string type):
template <typename Tree,
typename Path = typename Tree::path_type,
typename Key = typename Path::key_type,
typename Cmp = typename Tree::key_compare>
std::size_t unsupported(Tree const& tree, std::set<Key, Cmp> const& supported, Path prefix = "") {
if (tree.size()) {
std::size_t n = 0;
for (auto& node : tree) {
Path sub = prefix;
sub /= node.first;
n += unsupported(node.second, supported, sub);
}
return n;
} else {
if (!supported.count(prefix.dump()) && tree.template get_value_optional<std::string>())
return 1;
}
return 0;
}
You can use it like this:
if (auto n = unsupported(tree, {"ops1.i0", "ops1.r1", "ops2.s2"})) {
throw std::runtime_error(std::to_string(n) + " unsupported options");
}
Full Demo
Live On Coliru
#include <boost/algorithm/string.hpp>
#include <iostream>
#include <set>
enum class restricted { value1, value2 };
static inline std::istream& operator>>(std::istream& is, restricted& r) {
std::string v;
if (is >> std::ws >> v) {
if (boost::iequals("value1", v))
r = restricted::value1;
else if (boost::iequals("value2", v))
r = restricted::value2;
else
throw std::runtime_error("invalid restricted value");
}
return is;
}
static inline std::ostream& operator<<(std::ostream& os, restricted r) {
switch(r) {
case restricted::value1: return os << "value1";
case restricted::value2: return os << "value2";
default: return os << "invalid";
}
}
struct params {
int i0 = 1;
restricted r1 = restricted::value2;
std::string s2 = "some default";
};
#include <boost/property_tree/ini_parser.hpp>
#include <boost/lexical_cast.hpp>
#include <fstream>
namespace pt = boost::property_tree;
namespace translators {
template <typename T>
struct must_be_odd {
typedef T internal_type;
typedef T external_type;
boost::optional<T> get_value(const std::string& str) const {
if (str.empty()) return boost::none;
T v = boost::lexical_cast<T>(str);
if (v % 2 == 0)
throw std::runtime_error("value must be odd");
return boost::make_optional(v);
}
boost::optional<std::string> put_value(const T& i0) {
assert(i0 % 2); // assert that the value was odd
return boost::lexical_cast<std::string>(i0);
}
};
static const must_be_odd<int> i0;
}
template <typename Tree,
typename Path = typename Tree::path_type,
typename Key = typename Path::key_type,
typename Cmp = typename Tree::key_compare>
std::size_t unsupported(Tree const& tree, std::set<Key, Cmp> const& supported, Path prefix = "") {
if (tree.size()) {
std::size_t n = 0;
for (auto& node : tree) {
Path sub = prefix;
sub /= node.first;
n += unsupported(node.second, supported, sub);
}
return n;
} else {
if (!supported.count(prefix.dump()) && tree.template get_value_optional<std::string>())
return 1;
}
return 0;
}
params read_inifile(std::string filename) {
params p;
try {
pt::ptree tree;
std::ifstream file(filename);
read_ini(file, tree);
p.i0 = tree.get("ops1.i0", 1, translators::i0);
p.r1 = tree.get("ops1.r1", restricted::value2);
p.s2 = tree.get("ops1.s2", "some default");
if (auto n = unsupported(tree, {"ops1.i0", "ops1.r1", "ops2.s2"})) {
throw std::runtime_error(std::to_string(n) + " unsupported options");
}
} catch (std::exception const& e) {
std::cerr << "error: " << e.what() << "\n";
throw std::runtime_error("read_inifile");
}
return p;
}
pt::ptree to_ptree(params const& p) {
pt::ptree tree;
tree.put("ops1.i0", p.i0, translators::i0);
tree.put("ops1.r1", p.r1);
tree.put("ops1.s2", p.s2);
return tree;
}
int main() {
params const p = read_inifile("./testini.ini"); // get options from filename
write_ini("./used0.ini", to_ptree(p)); // save options to used.ini
std::cout << p.i0 << std::endl;
}
For input like
[ops1]
i0=17
i99=oops
[oops1]
also=oops
Prints
error: 2 unsupported options
terminate called after throwing an instance of 'std::runtime_error'
what(): read_inifile
And changing 17 to 18 prints:
error: value must be odd
terminate called after throwing an instance of 'std::runtime_error'
what(): read_inifile
On valid input, used0.ini will be written as expected:
[ops1]
i0=1
r1=value2
s2=some default
After giving this problem some more time, I found a suitable compact solution:
The key is to write a function that translates entries from the variables_map to the propTree depending on their datatype (thx to sehe for putting me on the right track):
void translate_variables_map_to_ptree(po::variables_map &vm, pt::ptree &propTree){
for(po::variables_map::iterator it=vm.begin(); it!=vm.end(); it++){
if( it->second.value().type() == typeid(int) ){ propTree.put<int>(it->first,vm[it->first].as<int>()); }
else if( it->second.value().type() == typeid(float) ){ propTree.put<float>(it->first,vm[it->first].as<float>()); }
else if( it->second.value().type() == typeid(double) ){ propTree.put<double>(it->first,vm[it->first].as<double>()); }
else if( it->second.value().type() == typeid(std::string) ){ propTree.put<std::string>(it->first,vm[it->first].as<std::string>()); }
else if( it->second.value().type() == typeid(size_t) ){ propTree.put<size_t>(it->first,vm[it->first].as<size_t>()); }
else{ printf("Error: unknown datatype. Abort!\n"); exit(EXIT_FAILURE); }
}
}
The full working example writes the correct inifile containing all read info:
/*
* g++ iniOps_test.cpp -Wall -std=c++11 -O3 -lboost_system -lboost_program_options -o iniOps_test.exe
*
*/
// C++11 & Boost libraries
#include <boost/program_options.hpp> // po::options_description, po::variables_map, ...
#include <boost/property_tree/ptree.hpp> // pt::ptree
#include <boost/property_tree/ini_parser.hpp> // write_ini()
#include <iostream> // cout
#include <fstream> // ofstream, ifstream
// namespaces
namespace po = boost::program_options;
namespace pt = boost::property_tree;
using namespace std;
struct params{
std::string s0;
int i0;
};
void read_inifile(params &p, po::variables_map &vm){
// initialize variables
int errorflag=0;
std::ifstream pthfnini("./testini.ini");
po::options_description inifile_options("Allowed inifile options");
try{
inifile_options.add_options()
("ops1.i0", po::value<int>(&p.i0)->default_value(1), "test integer")
("ops1.s0", po::value<std::string>(&p.s0)->default_value("default"), "test string")
;
;
po::store(po::parse_config_file(pthfnini, inifile_options), vm);
po::notify(vm);
}
catch(exception& e){
cerr << "error: " << e.what() << "\n";
errorflag=1;
}
pthfnini.close();
if(errorflag){ std::cout<<"--- program shutdown due to error in read_inifile ---"<<std::endl; exit(1); }
}
void translate_variables_map_to_ptree(po::variables_map &vm, pt::ptree &propTree){
for(po::variables_map::iterator it=vm.begin(); it!=vm.end(); it++){
if( it->second.value().type() == typeid(int) ){ propTree.put<int>(it->first,vm[it->first].as<int>()); }
else if( it->second.value().type() == typeid(float) ){ propTree.put<float>(it->first,vm[it->first].as<float>()); }
else if( it->second.value().type() == typeid(double) ){ propTree.put<double>(it->first,vm[it->first].as<double>()); }
else if( it->second.value().type() == typeid(std::string) ){ propTree.put<std::string>(it->first,vm[it->first].as<std::string>()); }
else if( it->second.value().type() == typeid(size_t) ){ propTree.put<size_t>(it->first,vm[it->first].as<size_t>()); }
else{ printf("Error: unknown datatype. Abort!\n"); exit(EXIT_FAILURE); }
}
}
int main(){
params p;
po::variables_map vm;
pt::ptree iniPropTree;
read_inifile(p,vm); // get options from inifile
translate_variables_map_to_ptree(vm,iniPropTree); // conversion from vm -> pt
pt::ini_parser::write_ini("./used0.ini",iniPropTree); // save options to used.ini
cout << p.i0 << endl;
cout << p.s0 << endl;
return 0;
}
Taking a variables_map vm from reading the commandline, it is also possible to update the values in the property tree (from reading the inifile) with:
string opsName = "ops1.i0"; if(vm.count(opsName)) p.i0 = vm[opsName].as<int>();
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?
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
}
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.
+1 for the Boost Karma by #Cubbi and the suggestion to "create your own streambuf-dervied type that does not make a copy, and give that to the constructor of a basic_istream<>.".
A more generic answer, though, is missing, and sits between these two.
It uses Boost Iostreams:
using string_buf = bio::stream_buffer<bio::back_insert_device<std::string> >;
Here's a demo program:
Live On Coliru
#include <boost/iostreams/device/back_inserter.hpp>
#include <boost/iostreams/stream_buffer.hpp>
namespace bio = boost::iostreams;
using string_buf = bio::stream_buffer<bio::back_insert_device<std::string> >;
// any code that uses ostream
void foo(std::ostream& os) {
os << "Hello world "
<< std::hex << std::showbase << 42
<< " " << std::boolalpha << (1==1) << "\n";
}
#include <iostream>
int main() {
std::string output;
output.reserve(100); // optionally optimize if you know roughly how large output is gonna, or know what minimal size it will require
{
string_buf buf(output);
std::ostream os(&buf);
foo(os);
}
std::cout << "Output contains: " << output;
}
Note that you can trivially replace the std::string withstd::wstring, or std::vector<char> etc.
Even better, you can use it with the array_sink device and have a fixed-size buffer. That way you can avoid any buffer allocation whatsoever with your Iostreams code!
Live On Coliru
#include <boost/iostreams/device/array.hpp>
using array_buf = bio::stream_buffer<bio::basic_array<char>>;
// ...
int main() {
char output[100] = {0};
{
array_buf buf(output);
std::ostream os(&buf);
foo(os);
}
std::cout << "Output contains: " << output;
}
Both programs print:
Output contains: Hello world 0x2a true
I implemented "outstringstream" class, which I believe does exactly what you need (see take_str() method). I partially used code from: What is wrong with my implementation of overflow()?
#include <ostream>
template <typename char_type>
class basic_outstringstream : private std::basic_streambuf<char_type, std::char_traits<char_type>>,
public std::basic_ostream<char_type, std::char_traits<char_type>>
{
using traits_type = std::char_traits<char_type>;
using base_buf_type = std::basic_streambuf<char_type, traits_type>;
using base_stream_type = std::basic_ostream<char_type, traits_type>;
using int_type = typename base_buf_type::int_type;
std::basic_string<char_type> m_str;
int_type overflow(int_type ch) override
{
if (traits_type::eq_int_type(ch, traits_type::eof()))
return traits_type::not_eof(ch);
if (m_str.empty())
m_str.resize(1);
else
m_str.resize(m_str.size() * 2);
const std::ptrdiff_t diff = this->pptr() - this->pbase();
this->setp(&m_str.front(), &m_str.back());
this->pbump(diff);
*this->pptr() = traits_type::to_char_type(ch);
this->pbump(1);
return traits_type::not_eof(traits_type::to_int_type(*this->pptr()));
}
void init()
{
this->setp(&m_str.front(), &m_str.back());
const std::size_t size = m_str.size();
if (size)
{
memcpy(this->pptr(), &m_str.front(), size);
this->pbump(size);
}
}
public:
explicit basic_outstringstream(std::size_t reserveSize = 8)
: base_stream_type(this)
{
m_str.reserve(reserveSize);
init();
}
explicit basic_outstringstream(std::basic_string<char_type>&& str)
: base_stream_type(this), m_str(std::move(str))
{
init();
}
explicit basic_outstringstream(const std::basic_string<char_type>& str)
: base_stream_type(this), m_str(str)
{
init();
}
const std::basic_string<char_type>& str() const
{
return m_str;
}
std::basic_string<char_type>&& take_str()
{
return std::move(m_str);
}
void clear()
{
m_str.clear();
init();
}
};
using outstringstream = basic_outstringstream<char>;
using woutstringstream = basic_outstringstream<wchar_t>;
Update: In the face of people's continued dislike of this answer, I thought I'd make an edit and explain.
No, there is no way to avoid a string copy (stringbuf has the same interface)
It will never matter. It's actually more efficient that way. (I will try to explain this)
Imagine writing a version of stringbuf that keeps a perfect, moveable std::string available at all times. (I have actually tried this).
Adding characters is easy - we simply use push_back on the underlying string.
OK, but what about removing characters (reading from the buffer)? We'll have to move some pointer to account for the characters we've removed, all well and good.
However, we have a problem - the contract we're keeping that says we'll always have a std::string available.
So whenever we remove characters from the stream, we'll need to erase them from the underlying string. That means shuffling all the remaining characters down (memmove/memcpy). Because this contract must be kept every time the flow of control leaves our private implementation, this in practice means having to erase characters from the string every time we call getc or gets on the string buffer. This translates to a call to erase on every << operation on the stream.
Then of course there's the problem of implementing the pushback buffer. If you pushback characters into the underlying string, you've got to insert them at position 0 - shuffling the entire buffer up.
The long and short of it is that you can write an ostream-only stream buffer purely for building a std::string. You'll still need to deal with all the reallocations as the underlying buffer grows, so in the end you get to save exactly one string copy. So perhaps we go from 4 string copies (and calls to malloc/free) to 3, or 3 to 2.
You'll also need to deal with the problem that the streambuf interface is not split into istreambuf and ostreambuf. This means you still have to offer the input interface and either throw exceptions or assert if someone uses it. This amounts to lying to users - we've failed to implement an expected interface.
For this tiny improvement in performance, we must pay the cost of:
developing a (quite complex, when you factor in locale management) software component.
suffering the loss of flexibility of having a streambuf which only supports output operations.
Laying landmines for future developers to step on.
I adapted the very good #Kuba answer to fix some issues (unfortunately he's currently unresponsive). In particular:
added a safe_pbump to handle 64 bit offsets;
return a string_view instead of string (internal string doesn't have the right size of the buffer);
resize the string to current buffer size on the move semantics take_str method;
fixed take_str method move semantics with init before return;
removed a useless memcpy on init method;
renamed the template parameter char_type to CharT to avoid ambiguity with basic_streambuf::char_type;
used string::data() and pointer arithmetic instead of possible undefined behavior using string::front() and string::back() as pointed by #LightnessRacesinOrbit;
Implementation with streambuf composition.
#pragma once
#include <cstdlib>
#include <limits>
#include <ostream>
#include <string>
#if __cplusplus >= 201703L
#include <string_view>
#endif
namespace usr
{
template <typename CharT>
class basic_outstringstream : public std::basic_ostream<CharT, std::char_traits<CharT>>
{
using traits_type = std::char_traits<CharT>;
using base_stream_type = std::basic_ostream<CharT, traits_type>;
class buffer : public std::basic_streambuf<CharT, std::char_traits<CharT>>
{
using base_buf_type = std::basic_streambuf<CharT, traits_type>;
using int_type = typename base_buf_type::int_type;
private:
void safe_pbump(std::streamsize off)
{
// pbump doesn't support 64 bit offsets
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47921
int maxbump;
if (off > 0)
maxbump = std::numeric_limits<int>::max();
else if (off < 0)
maxbump = std::numeric_limits<int>::min();
else // == 0
return;
while (std::abs(off) > std::numeric_limits<int>::max())
{
this->pbump(maxbump);
off -= maxbump;
}
this->pbump((int)off);
}
void init()
{
this->setp(const_cast<CharT *>(m_str.data()),
const_cast<CharT *>(m_str.data()) + m_str.size());
this->safe_pbump((std::streamsize)m_str.size());
}
protected:
int_type overflow(int_type ch) override
{
if (traits_type::eq_int_type(ch, traits_type::eof()))
return traits_type::not_eof(ch);
if (m_str.empty())
m_str.resize(1);
else
m_str.resize(m_str.size() * 2);
size_t size = this->size();
this->setp(const_cast<CharT *>(m_str.data()),
const_cast<CharT *>(m_str.data()) + m_str.size());
this->safe_pbump((std::streamsize)size);
*this->pptr() = traits_type::to_char_type(ch);
this->pbump(1);
return ch;
}
public:
buffer(std::size_t reserveSize)
{
m_str.reserve(reserveSize);
init();
}
buffer(std::basic_string<CharT>&& str)
: m_str(std::move(str))
{
init();
}
buffer(const std::basic_string<CharT>& str)
: m_str(str)
{
init();
}
public:
size_t size() const
{
return (size_t)(this->pptr() - this->pbase());
}
#if __cplusplus >= 201703L
std::basic_string_view<CharT> str() const
{
return std::basic_string_view<CharT>(m_str.data(), size());
}
#endif
std::basic_string<CharT> take_str()
{
// Resize the string to actual used buffer size
m_str.resize(size());
std::string ret = std::move(m_str);
init();
return ret;
}
void clear()
{
m_str.clear();
init();
}
const CharT * data() const
{
return m_str.data();
}
private:
std::basic_string<CharT> m_str;
};
public:
explicit basic_outstringstream(std::size_t reserveSize = 8)
: base_stream_type(nullptr), m_buffer(reserveSize)
{
this->rdbuf(&m_buffer);
}
explicit basic_outstringstream(std::basic_string<CharT>&& str)
: base_stream_type(nullptr), m_buffer(str)
{
this->rdbuf(&m_buffer);
}
explicit basic_outstringstream(const std::basic_string<CharT>& str)
: base_stream_type(nullptr), m_buffer(str)
{
this->rdbuf(&m_buffer);
}
#if __cplusplus >= 201703L
std::basic_string_view<CharT> str() const
{
return m_buffer.str();
}
#endif
std::basic_string<CharT> take_str()
{
return m_buffer.take_str();
}
const CharT * data() const
{
return m_buffer.data();
}
size_t size() const
{
return m_buffer.size();
}
void clear()
{
m_buffer.clear();
}
private:
buffer m_buffer;
};
using outstringstream = basic_outstringstream<char>;
using woutstringstream = basic_outstringstream<wchar_t>;
}
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?
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
}
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.
+1 for the Boost Karma by #Cubbi and the suggestion to "create your own streambuf-dervied type that does not make a copy, and give that to the constructor of a basic_istream<>.".
A more generic answer, though, is missing, and sits between these two.
It uses Boost Iostreams:
using string_buf = bio::stream_buffer<bio::back_insert_device<std::string> >;
Here's a demo program:
Live On Coliru
#include <boost/iostreams/device/back_inserter.hpp>
#include <boost/iostreams/stream_buffer.hpp>
namespace bio = boost::iostreams;
using string_buf = bio::stream_buffer<bio::back_insert_device<std::string> >;
// any code that uses ostream
void foo(std::ostream& os) {
os << "Hello world "
<< std::hex << std::showbase << 42
<< " " << std::boolalpha << (1==1) << "\n";
}
#include <iostream>
int main() {
std::string output;
output.reserve(100); // optionally optimize if you know roughly how large output is gonna, or know what minimal size it will require
{
string_buf buf(output);
std::ostream os(&buf);
foo(os);
}
std::cout << "Output contains: " << output;
}
Note that you can trivially replace the std::string withstd::wstring, or std::vector<char> etc.
Even better, you can use it with the array_sink device and have a fixed-size buffer. That way you can avoid any buffer allocation whatsoever with your Iostreams code!
Live On Coliru
#include <boost/iostreams/device/array.hpp>
using array_buf = bio::stream_buffer<bio::basic_array<char>>;
// ...
int main() {
char output[100] = {0};
{
array_buf buf(output);
std::ostream os(&buf);
foo(os);
}
std::cout << "Output contains: " << output;
}
Both programs print:
Output contains: Hello world 0x2a true
I implemented "outstringstream" class, which I believe does exactly what you need (see take_str() method). I partially used code from: What is wrong with my implementation of overflow()?
#include <ostream>
template <typename char_type>
class basic_outstringstream : private std::basic_streambuf<char_type, std::char_traits<char_type>>,
public std::basic_ostream<char_type, std::char_traits<char_type>>
{
using traits_type = std::char_traits<char_type>;
using base_buf_type = std::basic_streambuf<char_type, traits_type>;
using base_stream_type = std::basic_ostream<char_type, traits_type>;
using int_type = typename base_buf_type::int_type;
std::basic_string<char_type> m_str;
int_type overflow(int_type ch) override
{
if (traits_type::eq_int_type(ch, traits_type::eof()))
return traits_type::not_eof(ch);
if (m_str.empty())
m_str.resize(1);
else
m_str.resize(m_str.size() * 2);
const std::ptrdiff_t diff = this->pptr() - this->pbase();
this->setp(&m_str.front(), &m_str.back());
this->pbump(diff);
*this->pptr() = traits_type::to_char_type(ch);
this->pbump(1);
return traits_type::not_eof(traits_type::to_int_type(*this->pptr()));
}
void init()
{
this->setp(&m_str.front(), &m_str.back());
const std::size_t size = m_str.size();
if (size)
{
memcpy(this->pptr(), &m_str.front(), size);
this->pbump(size);
}
}
public:
explicit basic_outstringstream(std::size_t reserveSize = 8)
: base_stream_type(this)
{
m_str.reserve(reserveSize);
init();
}
explicit basic_outstringstream(std::basic_string<char_type>&& str)
: base_stream_type(this), m_str(std::move(str))
{
init();
}
explicit basic_outstringstream(const std::basic_string<char_type>& str)
: base_stream_type(this), m_str(str)
{
init();
}
const std::basic_string<char_type>& str() const
{
return m_str;
}
std::basic_string<char_type>&& take_str()
{
return std::move(m_str);
}
void clear()
{
m_str.clear();
init();
}
};
using outstringstream = basic_outstringstream<char>;
using woutstringstream = basic_outstringstream<wchar_t>;
Update: In the face of people's continued dislike of this answer, I thought I'd make an edit and explain.
No, there is no way to avoid a string copy (stringbuf has the same interface)
It will never matter. It's actually more efficient that way. (I will try to explain this)
Imagine writing a version of stringbuf that keeps a perfect, moveable std::string available at all times. (I have actually tried this).
Adding characters is easy - we simply use push_back on the underlying string.
OK, but what about removing characters (reading from the buffer)? We'll have to move some pointer to account for the characters we've removed, all well and good.
However, we have a problem - the contract we're keeping that says we'll always have a std::string available.
So whenever we remove characters from the stream, we'll need to erase them from the underlying string. That means shuffling all the remaining characters down (memmove/memcpy). Because this contract must be kept every time the flow of control leaves our private implementation, this in practice means having to erase characters from the string every time we call getc or gets on the string buffer. This translates to a call to erase on every << operation on the stream.
Then of course there's the problem of implementing the pushback buffer. If you pushback characters into the underlying string, you've got to insert them at position 0 - shuffling the entire buffer up.
The long and short of it is that you can write an ostream-only stream buffer purely for building a std::string. You'll still need to deal with all the reallocations as the underlying buffer grows, so in the end you get to save exactly one string copy. So perhaps we go from 4 string copies (and calls to malloc/free) to 3, or 3 to 2.
You'll also need to deal with the problem that the streambuf interface is not split into istreambuf and ostreambuf. This means you still have to offer the input interface and either throw exceptions or assert if someone uses it. This amounts to lying to users - we've failed to implement an expected interface.
For this tiny improvement in performance, we must pay the cost of:
developing a (quite complex, when you factor in locale management) software component.
suffering the loss of flexibility of having a streambuf which only supports output operations.
Laying landmines for future developers to step on.
I adapted the very good #Kuba answer to fix some issues (unfortunately he's currently unresponsive). In particular:
added a safe_pbump to handle 64 bit offsets;
return a string_view instead of string (internal string doesn't have the right size of the buffer);
resize the string to current buffer size on the move semantics take_str method;
fixed take_str method move semantics with init before return;
removed a useless memcpy on init method;
renamed the template parameter char_type to CharT to avoid ambiguity with basic_streambuf::char_type;
used string::data() and pointer arithmetic instead of possible undefined behavior using string::front() and string::back() as pointed by #LightnessRacesinOrbit;
Implementation with streambuf composition.
#pragma once
#include <cstdlib>
#include <limits>
#include <ostream>
#include <string>
#if __cplusplus >= 201703L
#include <string_view>
#endif
namespace usr
{
template <typename CharT>
class basic_outstringstream : public std::basic_ostream<CharT, std::char_traits<CharT>>
{
using traits_type = std::char_traits<CharT>;
using base_stream_type = std::basic_ostream<CharT, traits_type>;
class buffer : public std::basic_streambuf<CharT, std::char_traits<CharT>>
{
using base_buf_type = std::basic_streambuf<CharT, traits_type>;
using int_type = typename base_buf_type::int_type;
private:
void safe_pbump(std::streamsize off)
{
// pbump doesn't support 64 bit offsets
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47921
int maxbump;
if (off > 0)
maxbump = std::numeric_limits<int>::max();
else if (off < 0)
maxbump = std::numeric_limits<int>::min();
else // == 0
return;
while (std::abs(off) > std::numeric_limits<int>::max())
{
this->pbump(maxbump);
off -= maxbump;
}
this->pbump((int)off);
}
void init()
{
this->setp(const_cast<CharT *>(m_str.data()),
const_cast<CharT *>(m_str.data()) + m_str.size());
this->safe_pbump((std::streamsize)m_str.size());
}
protected:
int_type overflow(int_type ch) override
{
if (traits_type::eq_int_type(ch, traits_type::eof()))
return traits_type::not_eof(ch);
if (m_str.empty())
m_str.resize(1);
else
m_str.resize(m_str.size() * 2);
size_t size = this->size();
this->setp(const_cast<CharT *>(m_str.data()),
const_cast<CharT *>(m_str.data()) + m_str.size());
this->safe_pbump((std::streamsize)size);
*this->pptr() = traits_type::to_char_type(ch);
this->pbump(1);
return ch;
}
public:
buffer(std::size_t reserveSize)
{
m_str.reserve(reserveSize);
init();
}
buffer(std::basic_string<CharT>&& str)
: m_str(std::move(str))
{
init();
}
buffer(const std::basic_string<CharT>& str)
: m_str(str)
{
init();
}
public:
size_t size() const
{
return (size_t)(this->pptr() - this->pbase());
}
#if __cplusplus >= 201703L
std::basic_string_view<CharT> str() const
{
return std::basic_string_view<CharT>(m_str.data(), size());
}
#endif
std::basic_string<CharT> take_str()
{
// Resize the string to actual used buffer size
m_str.resize(size());
std::string ret = std::move(m_str);
init();
return ret;
}
void clear()
{
m_str.clear();
init();
}
const CharT * data() const
{
return m_str.data();
}
private:
std::basic_string<CharT> m_str;
};
public:
explicit basic_outstringstream(std::size_t reserveSize = 8)
: base_stream_type(nullptr), m_buffer(reserveSize)
{
this->rdbuf(&m_buffer);
}
explicit basic_outstringstream(std::basic_string<CharT>&& str)
: base_stream_type(nullptr), m_buffer(str)
{
this->rdbuf(&m_buffer);
}
explicit basic_outstringstream(const std::basic_string<CharT>& str)
: base_stream_type(nullptr), m_buffer(str)
{
this->rdbuf(&m_buffer);
}
#if __cplusplus >= 201703L
std::basic_string_view<CharT> str() const
{
return m_buffer.str();
}
#endif
std::basic_string<CharT> take_str()
{
return m_buffer.take_str();
}
const CharT * data() const
{
return m_buffer.data();
}
size_t size() const
{
return m_buffer.size();
}
void clear()
{
m_buffer.clear();
}
private:
buffer m_buffer;
};
using outstringstream = basic_outstringstream<char>;
using woutstringstream = basic_outstringstream<wchar_t>;
}
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?
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
}
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.
+1 for the Boost Karma by #Cubbi and the suggestion to "create your own streambuf-dervied type that does not make a copy, and give that to the constructor of a basic_istream<>.".
A more generic answer, though, is missing, and sits between these two.
It uses Boost Iostreams:
using string_buf = bio::stream_buffer<bio::back_insert_device<std::string> >;
Here's a demo program:
Live On Coliru
#include <boost/iostreams/device/back_inserter.hpp>
#include <boost/iostreams/stream_buffer.hpp>
namespace bio = boost::iostreams;
using string_buf = bio::stream_buffer<bio::back_insert_device<std::string> >;
// any code that uses ostream
void foo(std::ostream& os) {
os << "Hello world "
<< std::hex << std::showbase << 42
<< " " << std::boolalpha << (1==1) << "\n";
}
#include <iostream>
int main() {
std::string output;
output.reserve(100); // optionally optimize if you know roughly how large output is gonna, or know what minimal size it will require
{
string_buf buf(output);
std::ostream os(&buf);
foo(os);
}
std::cout << "Output contains: " << output;
}
Note that you can trivially replace the std::string withstd::wstring, or std::vector<char> etc.
Even better, you can use it with the array_sink device and have a fixed-size buffer. That way you can avoid any buffer allocation whatsoever with your Iostreams code!
Live On Coliru
#include <boost/iostreams/device/array.hpp>
using array_buf = bio::stream_buffer<bio::basic_array<char>>;
// ...
int main() {
char output[100] = {0};
{
array_buf buf(output);
std::ostream os(&buf);
foo(os);
}
std::cout << "Output contains: " << output;
}
Both programs print:
Output contains: Hello world 0x2a true
I implemented "outstringstream" class, which I believe does exactly what you need (see take_str() method). I partially used code from: What is wrong with my implementation of overflow()?
#include <ostream>
template <typename char_type>
class basic_outstringstream : private std::basic_streambuf<char_type, std::char_traits<char_type>>,
public std::basic_ostream<char_type, std::char_traits<char_type>>
{
using traits_type = std::char_traits<char_type>;
using base_buf_type = std::basic_streambuf<char_type, traits_type>;
using base_stream_type = std::basic_ostream<char_type, traits_type>;
using int_type = typename base_buf_type::int_type;
std::basic_string<char_type> m_str;
int_type overflow(int_type ch) override
{
if (traits_type::eq_int_type(ch, traits_type::eof()))
return traits_type::not_eof(ch);
if (m_str.empty())
m_str.resize(1);
else
m_str.resize(m_str.size() * 2);
const std::ptrdiff_t diff = this->pptr() - this->pbase();
this->setp(&m_str.front(), &m_str.back());
this->pbump(diff);
*this->pptr() = traits_type::to_char_type(ch);
this->pbump(1);
return traits_type::not_eof(traits_type::to_int_type(*this->pptr()));
}
void init()
{
this->setp(&m_str.front(), &m_str.back());
const std::size_t size = m_str.size();
if (size)
{
memcpy(this->pptr(), &m_str.front(), size);
this->pbump(size);
}
}
public:
explicit basic_outstringstream(std::size_t reserveSize = 8)
: base_stream_type(this)
{
m_str.reserve(reserveSize);
init();
}
explicit basic_outstringstream(std::basic_string<char_type>&& str)
: base_stream_type(this), m_str(std::move(str))
{
init();
}
explicit basic_outstringstream(const std::basic_string<char_type>& str)
: base_stream_type(this), m_str(str)
{
init();
}
const std::basic_string<char_type>& str() const
{
return m_str;
}
std::basic_string<char_type>&& take_str()
{
return std::move(m_str);
}
void clear()
{
m_str.clear();
init();
}
};
using outstringstream = basic_outstringstream<char>;
using woutstringstream = basic_outstringstream<wchar_t>;
Update: In the face of people's continued dislike of this answer, I thought I'd make an edit and explain.
No, there is no way to avoid a string copy (stringbuf has the same interface)
It will never matter. It's actually more efficient that way. (I will try to explain this)
Imagine writing a version of stringbuf that keeps a perfect, moveable std::string available at all times. (I have actually tried this).
Adding characters is easy - we simply use push_back on the underlying string.
OK, but what about removing characters (reading from the buffer)? We'll have to move some pointer to account for the characters we've removed, all well and good.
However, we have a problem - the contract we're keeping that says we'll always have a std::string available.
So whenever we remove characters from the stream, we'll need to erase them from the underlying string. That means shuffling all the remaining characters down (memmove/memcpy). Because this contract must be kept every time the flow of control leaves our private implementation, this in practice means having to erase characters from the string every time we call getc or gets on the string buffer. This translates to a call to erase on every << operation on the stream.
Then of course there's the problem of implementing the pushback buffer. If you pushback characters into the underlying string, you've got to insert them at position 0 - shuffling the entire buffer up.
The long and short of it is that you can write an ostream-only stream buffer purely for building a std::string. You'll still need to deal with all the reallocations as the underlying buffer grows, so in the end you get to save exactly one string copy. So perhaps we go from 4 string copies (and calls to malloc/free) to 3, or 3 to 2.
You'll also need to deal with the problem that the streambuf interface is not split into istreambuf and ostreambuf. This means you still have to offer the input interface and either throw exceptions or assert if someone uses it. This amounts to lying to users - we've failed to implement an expected interface.
For this tiny improvement in performance, we must pay the cost of:
developing a (quite complex, when you factor in locale management) software component.
suffering the loss of flexibility of having a streambuf which only supports output operations.
Laying landmines for future developers to step on.
I adapted the very good #Kuba answer to fix some issues (unfortunately he's currently unresponsive). In particular:
added a safe_pbump to handle 64 bit offsets;
return a string_view instead of string (internal string doesn't have the right size of the buffer);
resize the string to current buffer size on the move semantics take_str method;
fixed take_str method move semantics with init before return;
removed a useless memcpy on init method;
renamed the template parameter char_type to CharT to avoid ambiguity with basic_streambuf::char_type;
used string::data() and pointer arithmetic instead of possible undefined behavior using string::front() and string::back() as pointed by #LightnessRacesinOrbit;
Implementation with streambuf composition.
#pragma once
#include <cstdlib>
#include <limits>
#include <ostream>
#include <string>
#if __cplusplus >= 201703L
#include <string_view>
#endif
namespace usr
{
template <typename CharT>
class basic_outstringstream : public std::basic_ostream<CharT, std::char_traits<CharT>>
{
using traits_type = std::char_traits<CharT>;
using base_stream_type = std::basic_ostream<CharT, traits_type>;
class buffer : public std::basic_streambuf<CharT, std::char_traits<CharT>>
{
using base_buf_type = std::basic_streambuf<CharT, traits_type>;
using int_type = typename base_buf_type::int_type;
private:
void safe_pbump(std::streamsize off)
{
// pbump doesn't support 64 bit offsets
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47921
int maxbump;
if (off > 0)
maxbump = std::numeric_limits<int>::max();
else if (off < 0)
maxbump = std::numeric_limits<int>::min();
else // == 0
return;
while (std::abs(off) > std::numeric_limits<int>::max())
{
this->pbump(maxbump);
off -= maxbump;
}
this->pbump((int)off);
}
void init()
{
this->setp(const_cast<CharT *>(m_str.data()),
const_cast<CharT *>(m_str.data()) + m_str.size());
this->safe_pbump((std::streamsize)m_str.size());
}
protected:
int_type overflow(int_type ch) override
{
if (traits_type::eq_int_type(ch, traits_type::eof()))
return traits_type::not_eof(ch);
if (m_str.empty())
m_str.resize(1);
else
m_str.resize(m_str.size() * 2);
size_t size = this->size();
this->setp(const_cast<CharT *>(m_str.data()),
const_cast<CharT *>(m_str.data()) + m_str.size());
this->safe_pbump((std::streamsize)size);
*this->pptr() = traits_type::to_char_type(ch);
this->pbump(1);
return ch;
}
public:
buffer(std::size_t reserveSize)
{
m_str.reserve(reserveSize);
init();
}
buffer(std::basic_string<CharT>&& str)
: m_str(std::move(str))
{
init();
}
buffer(const std::basic_string<CharT>& str)
: m_str(str)
{
init();
}
public:
size_t size() const
{
return (size_t)(this->pptr() - this->pbase());
}
#if __cplusplus >= 201703L
std::basic_string_view<CharT> str() const
{
return std::basic_string_view<CharT>(m_str.data(), size());
}
#endif
std::basic_string<CharT> take_str()
{
// Resize the string to actual used buffer size
m_str.resize(size());
std::string ret = std::move(m_str);
init();
return ret;
}
void clear()
{
m_str.clear();
init();
}
const CharT * data() const
{
return m_str.data();
}
private:
std::basic_string<CharT> m_str;
};
public:
explicit basic_outstringstream(std::size_t reserveSize = 8)
: base_stream_type(nullptr), m_buffer(reserveSize)
{
this->rdbuf(&m_buffer);
}
explicit basic_outstringstream(std::basic_string<CharT>&& str)
: base_stream_type(nullptr), m_buffer(str)
{
this->rdbuf(&m_buffer);
}
explicit basic_outstringstream(const std::basic_string<CharT>& str)
: base_stream_type(nullptr), m_buffer(str)
{
this->rdbuf(&m_buffer);
}
#if __cplusplus >= 201703L
std::basic_string_view<CharT> str() const
{
return m_buffer.str();
}
#endif
std::basic_string<CharT> take_str()
{
return m_buffer.take_str();
}
const CharT * data() const
{
return m_buffer.data();
}
size_t size() const
{
return m_buffer.size();
}
void clear()
{
m_buffer.clear();
}
private:
buffer m_buffer;
};
using outstringstream = basic_outstringstream<char>;
using woutstringstream = basic_outstringstream<wchar_t>;
}