Reputation: 85
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
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
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