Reputation: 33
Newbie C++ programmer here. I'm trying to write a command line application that takes two arguments, an input file and an output file. However, if the input file or the output file name is "-", I need the program to read/output to standard input/output instead. My problem is that in C++, I don't know how to do this without the compiler not knowing that the input/output streams are initialized. Here's the code I have.
if(argv[1] == "-") {
istream input;
cout << "Using standard input" << endl;
}
else {
ifstream input;
cout << "Using input file " << argv[1] << endl;
input.open(argv[1], ios::in);
if(!input) {
cerr << "Cannot open input file '" << argv[1]
<< "', it either does not exist or is not readable." << endl;
return 0;
}
}
if(argv[2] == "-") {
ostream output;
cout << "Using standard output" << endl;
}
else {
ofstream output;
cout << "Using output file " << argv[2] << endl;
output.open(argv[2], ios::out);
if(!output) {
cerr << "Cannot open output file '" << argv[2] << "' for writing."
<< " Is it read only?" << endl;
return 0;
}
}
From here I cannot call the operator >> on input, because, I'm guessing, the compiler doesn't know it's been initialized.
Upvotes: 3
Views: 2541
Reputation: 258648
You can declare the members outside your conditionals, since ifstream
inherits istream
and ofstream
inherits ostream
. To avoid slicing, use pointers:
istream* input = NULL;
bool isFile = false;
if(argv[1] == "-") {
input = new istream;
}
else {
input = new ifstream;
isfile = true;
}
Then, wherever you want to use input
, you just cast it to the right type:
if (isFile)
{
ifstream* finput = (ifstream*)input;
}
This is not the only solution; there probably are cleaner ones
The thing is, you have to declare the stream outside of the block inside your conditional, so it doesn't go out of scope, since you want to use it outside.
Upvotes: 1
Reputation: 163357
You can use a reference to a stream, and then initialize it to refer to either a file stream or standard input or output. The initialization has to happen in a single command, though, so you'll have to declare the file streams even if you don't use them.
ifstream file_input;
istream& input = (strcmp(argv[1], "-") == 0) ? cin : file_input;
ofstream file_output;
ostream& output = (strcmp(argv[2], "-") == 0) ? cout : file_output;
Notice the &
in the declarations of input
and output
. They indicate that we're not declaring a separate stream object, but rather just declaring a reference to some other stream object, which we select conditionally based on the value of argv[x]
.
Then you can open the files, if you need to. The drawback is that we need to check for "-" strings twice instead of just once for each input or output.
if (strcmp(argv[1], "-") == 0) {
cout << "Using standard input" << endl;
} else {
cout << "Using input file " << argv[1] << endl;
file_input.open(argv[1]);
if (!file_input) {
cerr << "Cannot open input file '" << argv[1]
<< "', it either does not exist or is not readable." << endl;
return 1;
}
}
Thereafter, you can read from input
and write to output
, and the files or the standard I/O streams will be used.
Note the other changes I made to your code. First, I call strcmp
instead of using the ==
operator; the operator doesn't do what you think it does when comparing a char*
with a literal. Next, when opening the file fails, I return 1 instead of 0. Zero indicates a successful program, whereas non-zero tells the OS that the program failed.
Upvotes: 5