Riomare
Riomare

Reputation: 85

C++ std::istream to read from file

I am completely new to the c++ and I have problem with managing input. I have a program where I want to be possible to read input from console but also from the file. I have class with private field std::istream &input_stream; and in constructor I set this input_stream as std::cin, however when my program is run with flag -i-FILENAME I want input_stream to be that file.

class FlagsParser
{
private:
    std::istream &input_stream;

public:
    FlagsParser() : input_stream(std::cin){}
    void ParseFlags(int count,char *flags[]) -> arguments I get from main
    {
        for(int i =1;i<count;++i){
            std::string flag = flags[i];
            if(flag.rfind("-i",0) == 0){ // check if the arg starts with -i
                std::ifstream readFile(flag.substr(2)); 
                -> Here i want to set input_stream to readFile and I have no idea how.
                ...


What should I do ? Thank you for any help. (Maybe I am doing it completely wrong ?)

Upvotes: 0

Views: 7100

Answers (2)

Jiri Volejnik
Jiri Volejnik

Reputation: 1089

UPDATED: Sorry, I misunderstood the question. Here is what you can do:

Create the stream on the heap (using operator new) and assign it to a smart pointer, so that you don't have to free it manually. Later, check if the pointer is empty or not and return std::cin or the contents of the pointer, like this:

#include <iostream>
#include <fstream>

class FlagsParser {
    std::unique_ptr<std::istream> streamPtr;

public:
    FlagsParser() {
    }

    void ParseFlags(int count, const char* flags[]) {
        for( int i = 1; i < count; ++i) {
            std::string flag = flags[i];
            if (flag.rfind("-i", 0) == 0) {
                streamPtr.reset(new std::ifstream(flag.substr(2)));
            }
        }
    }

    std::istream& GetStream() {
        return (streamPtr.get() == nullptr) ? std::cin : *streamPtr.get();
    }
};

class Worker {
public:
    Worker() {
    }

    void Work(std::istream& stream) {
        // Do something with the stream
        std::istreambuf_iterator<char> begin(stream), end;
        std::string s(begin, end);
        std::cout << s;
    }
};

int main(int argc, const char * argv[]) {

    FlagsParser parser;
    parser.ParseFlags(argc, argv);

    Worker worker;
    worker.Work(parser.GetStream());

    return 0;
}

Upvotes: 1

Serge Ballesta
Serge Ballesta

Reputation: 148880

It is possible, but requires cautious programming.

As input_stream has to be a reference, it must refere to an existing object. There is no problem with std::cin which is a well known object defined by the standard library. To to have it refere a file stream, that damned file stream shall exist somewhere, and potentially different FlagParser objects could have different streams. A simple way to solve that is to add an auxilliary std::ifstream (a true object and not a ref) into the class and declare it before input_stream to be able to initialize input_stream with it. You could then decide to use it if it is an open stream, and use std::cin if it is not.

If the initialization of the ifstream is not trivial, it can be delegated to a member function:

class FlagsParser
{
private:
    std::ifstream inner_stream;
    std::istream &input_stream;

    // this method is expected to be called before full construction
    // of the object. It is not required to be static but it SHALL NOT
    // use any non static member. Making it static guarantees that.
    /* static */ std::ifstream build_stream(int count,char *flags[]) {
        std::ifstream ifs;
        // compute the file path from the args
        if (...) {
            ifs.open(path);
        }
        return ifs;
    }
public:
    Parser(int count, char *argv[]) :
            inner_stream(build_stream(count, argv),
            input_stream(inner_stream.is_open() ? inner_stream : std::cin) {
        // further initialization if required...
    }
};

You can then define your FlagParser in main:

int main(int argc, char *argv[]) {
    FlagParser parser(argc, argv);
    ...
}

Upvotes: 2

Related Questions