c++ get an string(serialized object) from ostream - c++

I have an Image class which has the following implementation
friend std::ostream& operator << ( std::ostream &os,Image* &img);
So I can serialize it by calling
ostm << img; // which will write an string into the ostream.
Is it possible to get that string out of the ostream or serialize it directly into an string object?
Thanks!
The solutions worked like a charm. Thank you so much!

Yes, you can use a std::ostringstream.
E.g.
#include <sstream>
#include <string>
#include <stdexcept>
std::string Serialize( const Image& img )
{
std::ostringstream oss;
if (!(oss << img))
{
throw std::runtime_error("Failed to serialize image");
}
return oss.str();
}

Presumably your actual object is an iostream, or a stringstream. If an iostream, you can do this:
std::iostream ss;
ss << "Some text\nlol";
std::string all_of_it((std::istreambuf_iterator<char>(ss)), std::istreambuf_iterator<char>());
std::cout << all_of_it; // Outputs: "Some text", then "lol" on a new line;
You need the istreambuf_iterator, hence the requirement for a bidirectional stream like iostream. Whenever you have extraction as well as insertion, you should use this, or a stringstream (or an fstream if working with files).
For a stringstream, just use its .str() member function to obtain its buffer as a string.

Related

How to assign istringstream and ifstream to an istream variable?

I want to have a variable of type istream which can hold either the contents of a file or a string. The idea is that if no file was specified, the variable of type istream would be assigned with a string.
std::ifstream file(this->_path)
and
std::istringstream iss(stringSomething);
to
std::istream is
I've tried just assigning them to the istream variable like I would with other objects that inherit from the same base class, but that didn't work.
How to assign istringstream and ifstream to an istream variable?
Base class pointers can point to derived class data. std::istringstream and std::ifstream both derived from std::istream, so we can do:
//Note that std::unique_ptr is better that raw pointers
std::unique_ptr<std::istream> stream;
//stream holds a file stream
stream = std::make_unique<std::ifstream>(std::ifstream{ this->_path });
//stream holds a string
stream = std::make_unique<std::istringstream>(std::istringstream{});
Now you just have to extract the content using
std::string s;
(*stream) >> s;
You can't assign to a std::istream but you can bind to a reference like this:
#include <string>
#include <sstream>
#include <fstream>
#include <iostream>
std::istringstream test_data(R"~(
some test data here
instead of in an external
file.
)~");
int main(int, char* argv[])
{
// if we have a parameter use it
std::string filename = argv[1] ? argv[1] : "";
std::ifstream ifs;
// try to open a file if we have a filename
if(!filename.empty())
ifs.open(filename);
// This will ONLY fail if we tried to open a file
// because the filename was not empty
if(!ifs)
{
std::cerr << "Error opening file: " << filename << '\n';
return EXIT_FAILURE;
}
// if we have an open file bind to it else bind to test_data
std::istream& is = ifs.is_open() ? static_cast<std::istream&>(ifs) : test_data;
// use is here
for(std::string word; is >> word;)
{
std::reverse(word.begin(), word.end());
std::cout << word << '\n';
}
}
Take a page out of the standard library: don't assign a value; assign a reference. That's probably what you want anyway.
std::istringstream iss(stringSomething);
std::istream& input(iss);
Because streams carry a lot of state, copying them is fraught with semantic questions. Consider for example what tellg should report in the copy after the original calls seekg. References by contrast answer the question transparently.
In C++, you cannot assign an object of type Child to a variable of type Parent, even if Child inherits from Parent. You can assign a pointer of type Child to a pointer of type Parent, however. You may want to consider dynamically allocating the objects.
In C++
std::istream is;
is an actual object, assigning to it will invoke the copy assignment operator which will copy the subobject of iss which is a std::istream into is and slice it. The example linked by LogicStuff will show that you need to assign a reference or pointer to iss like so:
std::istream &is_ref = iss;
The difference between values, references and pointers is fundamental to C++, I would advise getting a strong grasp of them.
std::istream can be constructed from a std::streambuf (basically the device that produces or consumes characters). All i/ostream objects have an associated std::streambuf and can be shared.
std::ifstream file(this->_path);
std::istringstream iss("str in gSo met hing");
std::istream A(iss.rdbuf()); // shares the same buffer device with iss
std::string str;
//////////////
while(A >> str) std::cout << str << " | "; //read everything from stream (~> iss)
std::cout << std::endl;
A = std::move(file);
while(A >> str) std::cout << str << " | "; //read from file, using same stream (~> file)

Copying streams in C++

The following program prints 0.
#include <ostream>
#include <string>
#include <sstream>
#include <iostream>
int main()
{
std::string subjectString("subject");
std::ostream tempStream(NULL);
tempStream << subjectString;
std::ostream& updatedStream = tempStream;
std::stringstream ss;
ss << updatedStream;
std::cout << ss.str() << std::endl;
return 0;
}
Why?
EDIT
As per Niall's sugesstion, I tried:
#include <ostream>
#include <string>
#include <sstream>
#include <iostream>
int main()
{
std::string subjectString("subject");
std::stringbuf buffer;
std::ostream tempStream(&buffer);
buffer.sputn(subjectString.c_str(), subjectString.size());
std::stringstream ss;
ss << tempStream;
std::cout << ss.str() << std::endl;
return 0;
}
Even this prints an address. Not the actual string.
The construction of std::ostream requires a buffer (not NULL).
In addition, basic_stream objects are not copyable;
basic_ostream( const basic_ostream& rhs ) = delete;
Reference;
http://en.cppreference.com/w/cpp/io/basic_ostream/basic_ostream
Try something more like this;
// ...
std::stringbuf buffer;
std::ostream tempStream(&buffer);
// ...
To associate a buffer with the stream.
Following on some of the discussions and edits;
In general, I would not directly manipulate the buffer, you should rather use the stream instead tempStream.write(...). The exact details are beyond the immediate question/problem; being the initialisation of the first stream with the buffer and streaming content into that stream. If all you want to do with the code is check if the data is in the buffer, then you could use tempStream.rdbuf()->sgetn(...).
You have already mentioned that this is part of a larger problem.
In the context of some the comments here and the original problem; this could be a case in which direct manipulation of the buffer is needed (in much the same way as the stream would). Your implementation would need to be well tested since this is not "the usual" way of working with streams, but it can work; .rdbuf() is the manner in which you can get to the underlying buffer. I don't have an exact snippet (maybe that's another question), but you can "clear the buffer" by resetting the position(s) of the put and get areas to be the same (see the positioning, put and get area functions of the buffer - std::ostream::seekp was mentioned as being used to deal with this). I think you standard library implementation of stringstream could also offer some useful hints.
Because tempStream has no stream to hold anything.
ss.str() returns NULL (0).

Copy contents of ostream to another ostream

I'm looking for a method to copy the contents from one ostream to another. I have the following code:
std::ostringsteam oss;
oss << "stack overflow";
{
//do some stuff that may fail
//if it fails, we don't want to create the file below!
}
std::ofstream ofstream("C:\\test.txt");
//copy contents of oss to ofstream somehow
Any help is appreciated!
Anything wrong with
ofstream << oss.str();
?
If you want to use the ostream base class then this isn't possible, as as far as an ostream is concerned anything that is written is gone forever. You will have to use something like:
// some function
...
std::stringstream ss;
ss << "stack overflow";
ss.seekg(0, ss.beg);
foo(ss);
...
// some other function
void foo(std::istream& is)
{
std::ofstream ofstream("C:\\test.txt");
ofstream << is.rdbuf();
}

Using streams to convert objects to strings

I have an object that overrides the stream operator << to act as a stringstream to make console printing easy.
cout << obj << endl;
I want to test this functionality in a unit test by comparing the string output with an expected string. I currently accomplish this by:
stringStream ss;
ss << obj;
string objStr = ss.str();
EXPECT_EQ(objStr, "expected string output");
This is not particular readable and it is certainly not succinct. Is there any easier, shorter and simpler way to get the string representation of an obj?
You could write a small, generic utility function that does that thing for you:
#include <utility>
#include <string>
#include <sstream>
template<typename T>
std::string make_string(T const& o)
{
std::stringstream ss;
ss << o;
return ss.str();
}
And use it this way:
EXPECTED_EQ(make_string(obj), "expected string output");

Strange behavior of stringstream passed by reference

For a project, I'd like to use stringstream to carry on data. To achieve this goal, I have to pass some stringstream as parameter to some function, but when I output the stringstreams, I see something like an address.
The code :
#include <iostream>
#include <sstream>
void doStuff(const std::iostream& msg)
{
std::cerr << msg << std::endl;
}
int main(void)
{
doStuff(std::stringstream("av"));
}
The output is :
0xbff4eb40
Can someone explains why I get an address when passing an rvalue ?
And why can't I pass a stringstream by value ?
You probably want to access the string on which the stringstream is storing its data:
void doStuff(const std::stringstream& msg)
{
std::cerr << msg.str() << std::endl;
}
What is happening in your code is that iostreams contain a void* operator which returns 0 if the stream contains any error or has reached EOF, and another value otherwise. This is usefull for error checking.
When you try to write you stream to std::cerr, the compiler realizes that the stream can be converted to a void* using that operator, and that a void* can be written to a ostream(the operator<< has been defined), and therefore uses it.
Note that i changed the method's signature so that it receives an std::stringstream as an argument, since std::iostream::str is not defined(this method is only available on string streams).
You get an address because it (like other streams) has a conversion to void * (which is primarily useful as a Boolean, to see whether reading/writing the stream has failed).
You can't pass it by value, because streams (again, in general, not just stringstreams) don't support copying and/or assigning.
To print the content of the stream, you could do something like:
void dostuff(std::iostream &msg) {
std::cerr << msg.rdbuf() << "\n";
}
Edit: Here's a complete demo program:
#include <iostream>
#include <sstream>
void show(std::ostream &os) {
std::cout << os.rdbuf() << "\n";
}
int main(){
std::stringstream test("whatever");
show(test);
return 0;
}
When I execute it, the output I get is the expected "whatever".