How to assign istringstream and ifstream to an istream variable? - c++

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)

Related

Can an istream variable be a class variable

While developing a program in C++ using VS2010 , can I define
std::istream streamRead(ReadBuf&); // struct ReadBuf : public std::streambuf declared before
and use this streamRead in multiple functions in my program?
If not, can anyone suggest me how to read a stream using getline. I have to read the same stream from different functions.
Thank you in advance.
EDIT:
The struct declared in my header file is as below:
struct ReadBuf : public std::streambuf
{
ReadBuf(PBYTE s,size_t n)
{
setg((char*)s,(char*) s,( char*)s + n);
}
};
I have a buffer in memory and the input to my program is its pointer and size. Using the above structure, I copy it to a streambuffer. Now I have to read this streambuffer line by line. This is my requirement.
For example some of my functions are:
int GetSessionN(int session_id,SessionDetail &N_session);
int GetInstanceId(string header,SessionDetail &N_session);
int GetDriverDetails(string body_data,SessionDetail &N_session);
I have to read the first n lines from the stream using GetSessionN and then the successive n lines in the next function and so on.
This is where I initialise the object of ReadBuf. I am not able to initialize it globally.
int SetupLogReader::ProcessLogFile(PBYTE &mem_ptr, ULONG &size)
{
string read;
ReadBuf buf(mem_ptr, size);
istream streamRead(&buf);// Not able use StreamRead declared in header here.
}
you should not copy the stream when returning it in the function but reference it, i.e:
std::istream &streamRead(ReadBuf&){
if (_stream == null){
// create stream
_stream = [newly created stream];
}
return _stream;
}
Edit:
You could also use std::istringstream as it already provides the functionality you are looking for:
from istringstream manual:
std::string stringvalues = "line1\nline2";
std::istringstream iss (stringvalues);
for (int n=0; n<2; n++)
{
char val[256];
iss.getline(val, 256);
std::cout << val << '\n';
}

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();
}

SIGABRT in binary read/write

I wrote a very small code snippet and have already gotten the following error:
malloc: *** error for object 0x100100080: pointer being freed was not allocated
Problem is, I have no idea what pointer the compiler's talking about. I pass a variable in by address to the read/write functions, but I never freed it as far as I know. Where's the error in my code? I ran it with Leaks and Zombies, but got nothing.
Here's my program:
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
class Bank
{
private:
string __name;
public:
Bank()
{
__name = "";
}
Bank(string name)
{
__name = name;
}
string getName() const { return __name; }
};
int main (int argc, char * const argv[])
{
Bank bank("Bank of America");
Bank bank2;
cout << "Bank1: " << bank.getName() << endl;
string filename = bank.getName() + ".bank";
ofstream fout(filename.c_str(), ios::binary);
if (fout.good())
fout.write((char *)&bank, sizeof(bank));
fout.close();
ifstream fin(filename.c_str(), ios::binary);
if (fin.good())
fin.read((char *)&bank2, sizeof(bank2));
fin.close();
cout << "Bank2: " << bank2.getName() << endl;
return 0;
}
You can't read an object that contains a std::string (or anything that's not Plain Ol' Data) with fin.read()--
The object is read and written as a stream of bytes, but std:string contains a pointer to memory that is stored elsewhere and is not written with your fout.write() and is not initialized properly with your fin.read()
It is because it is not initialized properly with your fin.read() that you are getting the heap error; when the object goes out of scope, the destructor of the improperly initialized std::string is being called, and trying to free memory that it doesn't own.
You probably want to write a custom i/o method for your object and save or load it piece-by-piece. For a shortcut to doing this, use the Boost serialization library.
Because your Bank class contains a std::string, you can't read/write it as binary like you are thinking. A std::string has internal pointers. If you write it as binary, you are just going to be writing pointers and not the actual string contents. Likewise, when you read the string, you are going to be reading a pointer. In this case, you end up making both your bank and bank2 objects have strings which point to the same memory, so when that memory is freed it gets freed twice.
You'll need to have some other way of writing your bank data to a file. In this case, a simple ASCII file with the bank name would be fine.
You cannot do what you are doing, simply because std::string cannot be copied like that. Internally a string object allocates memory and a simple copy of the outer structure doesn't do what you expect.
You need to serialize this structure properly.
Don't use underscores, please
Pass objects by reference: Bank(string& name), please
This is evil: fout.write((char *)&bank, sizeof(bank));
You may want to write << and >> ostream operators of your Bank class.
For example:
friend std::ostream& operator<<(std::ostream &out, const Bank& b);
friend std::istream& operator>>(std::istream &out, const Bank& b);
Members functions write of ostream and read of istream are specifically designed to input and output binary data. If you do want to manipulate binary data, use the following:
ifstream fin(filename.c_str(), ios::in|ios::binary|ios::ate);
size = fin.tellg();
memblock = new char [size];
fin.seekg(0, ios::beg);
if (fin.good()){
fin.read(memblock, size);
fin.close();
}
delete[] memblock;

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

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.

problem passing in istream argument to a class constructor

I have the following code in my header file:
class Factovisors {
public:
Factovisors(std::istream& strm):strm_(strm)
{
}
void run()
{
unsigned int n,m;
while (!strm_.eof()) {
strm_ >> n >> m;
if (isFact(n,m))
std::cout << m << " divides " << n << "!\n";
}
}
std::istream strm_;
};
My .cpp file has the following code.
std::ifstream strm("factovisor.test");
Factovisors facto(strm);
facto.run();
strm.close();
The error my compiler gives me is:
std::ios::basic_ios(const std::ios &) is not accessible from
std::istream::basic_istream(const std::istream &)
I imagine I am missing something really obvious. So any help would be greatly appreciated.
The problem is that istream is an "interface". It has pure virtual functions, so it doesn't make sense to have a copy of it. What you might do is to keep a reference to the passed stream:
std::istream& strm_;
strm_ could be ifstream or istringstream or any input stream derived from istream.
You can't copy-construct a stream because the base-class ios has its copy ctor private. Try making the stream member a reference, rather than a standalone object.
You are trying to store a copy of the stream. This will not work, since streams are not copyable. Best you can do is store a reference or a pointer.
However, if only one method is going to use the stream, just pass a reference to this method.
Other problems:
while (!strm_.eof()) {
strm_ >> n >> m;
if (isFact(n,m))
Eof is set when an attempt to read data fails because of this. As it is you are bound to read the last entry twice. Instead:
while (strm >> n >> m )
if (isFact(n, m)