0

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 ?)

1

2 Answers 2

2

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);
    ...
}
Sign up to request clarification or add additional context in comments.

1 Comment

For safety, make the member function build_stream static.
1

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

3 Comments

auto_ptr was deprecated in C++11 and removed in C++17
Thanks for pointing it out, use unique_ptr or whatever is recommended now.
unique_ptr, which required the language changes of C++11, does what auto_ptr should have done (but couldn't)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.