How to assign istringstream and ifstream to an istream variable?
Asked Answered
A

6

13

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?

Abundance answered 5/8, 2016 at 21:42 Comment(1)
Very similar, but with output streams.Hoggish
S
11

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;
Selfconfessed answered 5/8, 2016 at 21:55 Comment(2)
I would probably use std::unique_ptr<std::istream> stream_owner; and then std::istream& stream = *stream_owner;, allowing the familiar stream >> s syntax.Spaniard
The make_unique() calls can be simplified a little: stream = std::make_unique<std::ifstream>(_path); and stream = std::make_unique<std::istringstream>(); Also note that std::unique_ptr is new in C++11, but std::make_unique is new in C++14.Deeplaid
A
8

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';
    }
}
Ardor answered 6/8, 2016 at 1:12 Comment(1)
This is exactly what I was looking for: a complete example for choosing a default stream (e.g. std::cin) if a file was not supplied, and to assign it to a variable of type std::istream.Dissimilitude
V
1

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.

Variate answered 5/8, 2016 at 21:57 Comment(0)
N
1

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.

Nashoma answered 5/8, 2016 at 21:57 Comment(0)
F
1

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.

Finagle answered 5/8, 2016 at 21:59 Comment(0)
S
1

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)
Suribachi answered 5/8, 2016 at 22:15 Comment(1)
In the second case, shouldn't you call A.rdbuf(file.rdbuf()); to share the buffer instead of transferring ownership?Deeplaid

© 2022 - 2024 — McMap. All rights reserved.