C++: what benefits do string streams offer? - c++

could any one tell me about some practical examples on using string streams in c++, i.e. inputing and outputing to a string stream using stream insertion and stream extraction operators?

You can use string streams to convert anything that implements operator << to a string:
#include <sstream>
template<typename T>
std::string toString(const T& t)
{
std::ostringstream stream;
stream << t;
return stream.str();
}
or even
template <typename U, typename T>
U convert(const T& t)
{
std::stringstream stream;
stream << t;
U u;
stream >> u;
return u;
}

I use them mostly as memory buffers, in creating messages:
if(someVector.size() > MAX_SIZE)
{
ostringstream buffer;
buffer << "Vector should not have " << someVector.size() << " eleements";
throw std::runtime_error(buffer.str());
}
or to construct complex strings:
std::string MyObject::GenerateDumpPath()
{
using namespace std;
std::ostringstream dumpPath;
// add the file name
dumpPath << "\\myobject."
<< setw(3) << setfill('0') << uniqueFileId
<< "." << boost::lexical_cast<std::string>(state)
<< "_" << ymd.year
<< "." << setw(2) << setfill('0') << ymd.month.as_number()
<< "." << ymd.day.as_number()
<< "_" << time.hours()
<< "." << time.minutes()
<< "." << time.seconds()
<< ".xml";
return dumpPath.str();
}
It is useful because it brings all the extensibility of std::streams to using character buffers (ostreams extensibility and locales support, buffer memory management is hidden and so on).
Another example I've seen was the error reporting in gsoap library, using dependency injection: soap_stream_fault takes an ostream& parameter to report error messages in.
If you want you can pass it std::cerr, std::cout or an std::ostringstream implementation (I use it with a std::ostringstream implementation).

They can be used anywhere a normal stream can be used.
So in situations where you were reading from a file you could potentially read from a string stream.
void compile(std::istream& str)
{
CPlusPlusLexer lexer(str);
CPlusPlusParser parser(lexer);
BackEnd backend(parser);
backend.compile();
}
int main()
{
std::fstream file("Plop.cpp");
compile(file);
std::stringstream test("#include <iostream>\n int main() { std::cout << \"H World\n\";}");
compile(test);
}

Besides advantages there is one point to carefully consider if you use gcc 4.3.1. I didn't checked preceding versions of gcc.

Related

C++ how to cout and write at the same time

After reading this: C++ Writing to file and Console Output at the same time with simple code , I am trying to write a function to handle what I have in the for-loop. But I don't know how to pass a piece of code like this :setw(3) << right << get<0>(*it_party) << setw(9) << "" altogether as an argument into a function so that both cout and file can use it.
The helper function I am trying to write:
void print(std::ostream &os1, std::ostream &os2, const [I don't know what to put in here])
{
os1 << the argument; // cout
os2 << the argument; // file
}
ofstream file;
file.open("test.txt");
for (auto it_party = parties_.begin(); it_party != parties_.end(); ++it_party) {
cout << setw(3) << right << get<0>(*it_party) << setw(9) << "";
file << setw(3) << right << get<0>(*it_party) << setw(9) << "";
cout << setw(7) << left << get<1>(*it_party) << setw(1) << "";
file << setw(7) << left << get<1>(*it_party) << setw(1) << "";
...
...
}
If you want to string outputs together like that, you'll probably find it easier to use some kind of "tee" class:
class Tee {
private:
std::ostream &os1, &os2;
public:
Tee(std::ostream &os1, std::ostream &os2) : os1(os1), os2(os2) { }
template <typename T>
Tee& operator<<(const T &thing) {
os1 << thing;
os2 << thing;
return *this;
}
};
This'll take a template argument, so it won't care whether you're passing right or setw(3). You can use it like:
Tee t(std::cout, std::cout);
t << setw(10) << "right" << " Thing" << setw(9);
You can easily do this using std::stringstream. I don't know what get<>() is but here is a basic example:
#include <string>
#include <iostream>
#include <sstream>
#include <fstream>
void fn(std::string output) {
std::ofstream file("file.txt");
std::cout << output;
file << output;
}
int main() {
std::stringstream buffer;
buffer << "some random text" << std::endl;
fn(buffer.str());
return 0;
}
Unfortunately, you cannot pass all that stuff as only one argument. But there are some advanced techniques, that can help you, such as variadic templates and C++17 fold expressions.
Variadic templates are a feature that allows you, for example, to pass an unknown at compile-time amount of arguments of different types. You can see some examples of such in a standard library, such as std::tuple.
Fold expressions are yet another feature used in combination with the former one. It allows you to write pretty simple variadic-templated code:
template<typename ...Args>
void printer(const Args &... args) {
(std::cout << ... << args) << '\n';
}
Here we see a function, that takes some const & to some arguments (we don't know their count or distinct types), but we have such fancy thing as args, a parameter pack, that represents our arguments, and Args as their types "packed" together;
You can use that to pass arbitrary arguments to your streams:
template<typename ...Args>
void printer(const Args &... args) {
(std::cout << ... << args);
(file << ... << args);
}
There are some other things to improve in this solution, such as perfect forwarding, but these are for further study.

C++ << no operator found

string toString() {
std::stringstream punkte;
std::stringstream name;
std::cout << name << "hat" << punkte << "Punkte" << '\n'
return 0;
}
At this line of code. I'm receiving the error C++ << no operator found
I can't figure out what my mistake is. I have read and tried different solutions. But nothing works. Can somebody please help?
std::cout << name << "hat" << punkte << "Punkte" << '\n';
I also included this in my code:
#include <string> // std::string
#include <iostream> // std::cout
#include <sstream> // std::stringstream, std::stringbuf
#include <fstream>
There is no overload of operator<<() that will format a std::stringstream to a std::ostream. There error does not lie.
You are trying to call operator "<<" with a stringstream parameter. In other words:
std::cout << name;
Is equivalent to:
std::cout.operator<<(name);
And that operator<<(const std::stringstream&) function doesn't exists.
I think that what you want to do is assign each stringstream their values and then print both, isn't?
string toString()
{
std::stringstream punkte;
std::stringstream name;
name << "hat";
punkte << "Punkte";
std::cout << name.str() << punkte.str() << std::endl;
return name.str();
}
Be careful with your return value, and remember that a std::stringstream is not a std::string. If you want to retrieve the std:string in the stream, you must call the str() method.

Feeding to std::stringstream and simultaneously passing it as argument to a function c++

I have the following functions
void Assembly::push_back(stringstream& ss){
instructions->push_back(ss.str());
}
std::ostream& pad(std::ostream& os) {
return os <<"\t"<<std::left<< std::setfill(' ') << std::setw(8);
}
where instructions is a list of strings. I want to do sth like
Assembly outfile;
stringstream ss;
outfile.push_back(ss<<pad<<"addiu"<<"$sp, $sp, -8");
but I get an error from the compiler
no known conversion for argument 1 from 'std::basic_ostream<char>' to 'std::stringstream& {aka std::basic_stringstream<char>&}'
I tried changing the definition of push_back to take std::basic_ostream<char> but it still did not compile.
Basically what I want to do is be able to push_back a string which has the alignment that pad adds. It is also essential that I am able to call Assembly::push_back in a single line (I know that
Assembly outfile;
stringstream ss;
ss<<pad<<"addiu"<<"$sp, $sp, -8";
outfile.push_back(ss);
works, but it requires two lines of code to make the proper call). I am also open to solutions which overload the operator<< for Assembly or allow me to accomplish the required behavior in a single line.
Make your pad() take and return a std::stringstream, and then also add this:
template <typename T>
std::stringstream &
operator<<(std::stringstream &ss, const T &o) {
static_cast<std::ostream &>(ss) << o; // Prevent recursion.
return ss;
}
Here is what I think the problem is. This won't work because the return value of ss << anything is always an std::ostream& which is a parent of std::stringstream and does not satisfy the argument for your push_back() function.
void push_back(const std::stringstream& ss)
{
std::cout << "push: " << ss.str() << '\n';
}
int main(int, char* argv[])
{
std::stringstream ss;
// what gets sent to the function is an std::ostream&
// which is an incompatible type.
push_back(ss << "one " << 2 << ' ' << 3.0); // fail
}
The way to fix this is to create an overload for operator<<(std::stringstream& that returns the correct type:
template<typename DataType>
std::stringstream& operator<<(std::stringstream& ss, const DataType& data)
{
static_cast<std::ostream&>(ss) << data;
return ss;
}
Now calling << on a std::stringstream will return a std::stringstream& which is acceptable as your push_back() parameter.
void push_back(const std::stringstream& ss)
{
std::cout << "push: " << ss.str() << '\n';
}
int main(int, char* argv[])
{
std::stringstream ss;
// Now the << returns the correct type (std::stringstream&)
push_back(ss << "one " << 2 << ' ' << 3.0); // good
}

C++ Serializing a std::map to a file

I have a C++ STL map, which is a map of int and customType.
The customType is a struct, which has string and a list of string, How can i serialize this to a file.
sample struct:
struct customType{
string;
string;
int;
list<string>;
}
If you are not afraid of BOOST, try BOOST Serialize:
(template code, here can be some errors...)
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/serialization/map.hpp>
#include <boost/serialization/string.hpp>
#include <boost/serialization/list.hpp>
struct customType{
string string1;
string string2;
int i;
list<string> list;
// boost serialize
private:
friend class boost::serialization::access;
template <typename Archive> void serialize(Archive &ar, const unsigned int version) {
ar & string1;
ar & string2;
ar & i;
ar & list;
}
};
template <typename ClassTo>
int Save(const string fname, const ClassTo &c)
{
ofstream f(fname.c_str(), ios::binary);
if (f.fail()) return -1;
boost::archive::binary_oarchive oa(f);
oa << c;
return 0;
}
Usage:
Save< map<int, customType> >("test.map", yourMap);
A simple solution is to output each member on a line on its own, including all the strings in the list. Each record start with the key to the map, and ends with a special character or character sequence that can not be in the list. This way you can read one line at a time, and know the first line is the map key, the second line the first string in the structure and so on, and when you reach your special record-ending sequence you know the list is done and it's time for the next item in the map. This scheme makes the files generated readable, and editable if you need to edit them outside the program.
C++ doesn't have reflection capabilities like Java and others, so there's no 'automatic' way of doing that. You'll have to do all the work yourself: open the file, output each element in a loop, and close the file. Also there's no standard format for the file, you'd need to define one that meets your needs. Of course, there are libraries out there to help in this, but they aren't part of the language. Take a look at this question:
Is it possible to automatically serialize a C++ object?
Also take a look at:
http://s11n.net/
If you are asking this, then probably you already know that you cannot serialize this by means of:
file.write( (const char *) &mapOfCustom, sizeof( mapOfCustom ) );
The problem has to do with complex objects (and in C++, even a string variable is a complex object), i.e., those objects that are not self-contained. Actually, even simple serialization has problems, which range from platform compatibilty to even compiler compatibilty (different paddings, etc.).
One way to go is use a simple XML library such as tinyXML:
http://www.grinninglizard.com/tinyxml/
And write save to XML, and restore from XML procedures.
You can try this: cxx-prettyprint
Hi I wrote a standalone C11 header to achieve this. Your example
of a map of custom classes, I just added - to make sure it worked 8)
https://github.com/goblinhack/simple-c-plus-plus-serializer
#include "c_plus_plus_serializer.h"
class Custom {
public:
int a;
std::string b;
std::vector c;
friend std::ostream& operator<<(std::ostream &out,
Bits my)
{
out << bits(my.t.a) << bits(my.t.b) << bits(my.t.c);
return (out);
}
friend std::istream& operator>>(std::istream &in,
Bits my)
{
in >> bits(my.t.a) >> bits(my.t.b) >> bits(my.t.c);
return (in);
}
friend std::ostream& operator<<(std::ostream &out,
class Custom &my)
{
out << "a:" << my.a << " b:" << my.b;
out << " c:[" << my.c.size() << " elems]:";
for (auto v : my.c) {
out << v << " ";
}
out << std::endl;
return (out);
}
};
static void save_map_key_string_value_custom (const std::string filename)
{
std::cout << "save to " << filename << std::endl;
std::ofstream out(filename, std::ios::binary );
std::map< std::string, class Custom > m;
auto c1 = Custom();
c1.a = 1;
c1.b = "hello";
std::initializer_list L1 = {"vec-elem1", "vec-elem2"};
std::vector l1(L1);
c1.c = l1;
auto c2 = Custom();
c2.a = 2;
c2.b = "there";
std::initializer_list L2 = {"vec-elem3", "vec-elem4"};
std::vector l2(L2);
c2.c = l2;
m.insert(std::make_pair(std::string("key1"), c1));
m.insert(std::make_pair(std::string("key2"), c2));
out << bits(m);
}
static void load_map_key_string_value_custom (const std::string filename)
{
std::cout << "read from " << filename << std::endl;
std::ifstream in(filename);
std::map< std::string, class Custom > m;
in >> bits(m);
std::cout << std::endl;
std::cout << "m = " << m.size() << " list-elems { " << std::endl;
for (auto i : m) {
std::cout << " [" << i.first << "] = " << i.second;
}
std::cout << "}" << std::endl;
}
void map_custom_class_example (void)
{
std::cout << "map key string, value class" << std::endl;
std::cout << "============================" << std::endl;
save_map_key_string_value_custom(std::string("map_of_custom_class.bin"));
load_map_key_string_value_custom(std::string("map_of_custom_class.bin"));
std::cout << std::endl;
}
Output:
map key string, value class
============================
save to map_of_custom_class.bin
read from map_of_custom_class.bin
m = 2 list-elems {
[key1] = a:1 b:hello c:[2 elems]:vec-elem1 vec-elem2
[key2] = a:2 b:there c:[2 elems]:vec-elem3 vec-elem4
}
Let me know if this helps - or you find bugs. It's quite a simple serializer and really just a learning tool for me. Heavier weight approaches like Cereal might work for you better.

is there a good way to combine stream manipulators?

If I wanted to output a fixed width hex number with 4 digits on a stream, I would need to do something like this:
cout << "0x" << hex << setw(4) << setfill('0') << 0xABC;
which seems a bit long winded. Using a macro helps:
#define HEX(n) "0x" << hex << setw(n) << setfill('0')
cout << HEX(4) << 0xABC;
Is there a better way to combine the manipulators?
Avoid the macros when you can! They hide code, making things hard to debug, don't respect scope, etc.
You can use a simple function as KenE provided. If you want to get all fancy and flexible, then you can write your own manipulator:
#include <iostream>
#include <iomanip>
using namespace std;
ostream& hex4(ostream& out)
{
return out << "0x" << hex << setw(4) << setfill('0');
}
int main()
{
cout << hex4 << 123 << endl;
}
This makes it a little more general. The reason the function above can be used is because operator<< is already overloaded like this: ostream& operator<<(ostream&, ostream& (*funtion_ptr)(ostream&)). endl and some other manipulators are also implemented like this.
If you want to allow the number of digits to be specified at runtime, we can use a class:
#include <iostream>
#include <iomanip>
using namespace std;
struct formatted_hex
{
unsigned int n;
explicit formatted_hex(unsigned int in): n(in) {}
};
ostream& operator<<(ostream& out, const formatted_hex& fh)
{
return out << "0x" << hex << setw(fh.n) << setfill('0');
}
int main()
{
cout << formatted_hex(4) << 123 << endl;
}
If the size can be determined at compile-time, however, might as well just use a function template [thanks to Jon Purdy for this suggestion]:
template <unsigned int N>
ostream& formatted_hex(ostream& out)
{
return out << "0x" << hex << setw(N) << setfill('0');
}
int main()
{
cout << formatted_hex<4> << 123 << endl;
}
Why a macro - can't you use a function instead?
void write4dhex(ostream& strm, int n)
{
strm << "0x" << hex << setw(4) << setfill('0') << n;
}
In C++20 you'll be able to use std::format to make this much less verbose:
std::cout << std::format("0x{:04x}", 0xABC);
Output:
0x0abc
You can also easily reuse the format string by storing it in a constant.
In the meantime you can use the {fmt} library, std::format is based on. {fmt} also provides the print function that makes this even easier and more efficient (godbolt):
fmt::print("0x{:04x}", 0xABC);
Disclaimer: I'm the author of {fmt} and C++20 std::format.