Reputation: 19251
This code works as desired for the most part, which is to prompt the user for a single character, perform the associated action, prompt the user to press return, and repeat. However, when I enter ^D (EOF) at the prompt, an infinite loop occurs. I am clearing the error state via std::cin.clear() and calling std::cin.ignore(...) to clear the buffer. What could be causing the infinite loop?
#include <iostream>
#include <limits>
void wait()
{
std::cout << std::endl << "press enter to continue.";
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::cin.clear();
std::cin.get();
}
int main()
{
char response;
while (true)
{
std::cout << "enter a character at the prompt." << std::endl << "> ";
std::cin >> response;
switch (response)
{
case 'q':
exit(0);
break;
}
wait();
}
}
I am running this in the Mac OS X terminal, if it matters.
UPDATE: What I am really asking here is, when the user enters EOF (^D) at the prompt, how do I (a) detect it and (b) reset the stream so that the user can continue to enter data.
The following example is different from the code above, but illustrates the same principle of clearing the stream after a ^D has been detected and continuing to read from that stream.
> a you entered: a > b you entered: b > ^D you entered EOF > c you entered: c ...
Upvotes: 3
Views: 4773
Reputation: 551
The question does not really make sense for standard input. It will be hard to read something from standard input after that stream has ended -- you'll have to re-open it somehow, but there is no way to re-open standard input. It might be connected to a pipe, or to a file, or to a terminal -- and there's no behaviour suitable for all of these.
So you're going to be reading explicitly from the terminal, I assume. On UN*X systems, that means reading /dev/tty, and re-opening it when needed. Here's a simple example that does it; most error-checking omitted.
// Warning: UN*X-specific
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
for(unsigned i=0; ; i++) {
ifstream tty("/dev/tty");
if (! tty) {
cerr << "Failed to open TTY" << endl;
return 2;
}
string s;
while (getline(tty,s))
cout << i << ": " << s << endl;
}
return 0; // (unreached)
}
Upvotes: 2
Reputation: 1596
while ((std::cout << "Enter a character at the prompt ")
&& (!(std::cin >> response) || response =='q')) {
std::cout << "Not a valid input";
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
Upvotes: 0
Reputation: 18984
As mentioned, you need to make sure the stream is not in a bad state. I would change while condition to use good(). Don't just check EOF as there are several ways a stream can become "bad" other than EOF.
while (std::cin.good()) {...
Upvotes: 0
Reputation: 793017
You should always check whether any of a stream's failure flags are set after calling formatted extraction operation, in your example you are checking response
without checking whether response
was correctly extracted.
Also, you are using std::endl
in your prompt output where it doesn't make sense. std::endl
prints \n
and then flushes the buffer, but you then immediately print more characters so the flush is redundant. As cin
and cout
are (usually) tied, calling an input function for std::cin
will cause std::cout
to be flushed in any case so you may as well put a \n
into your prompt string and save on the verbose extra <<
operators.
Why not make a prompting function that prints the prompt, retrieves the input an returns a reference to the stream so that you can test it for success using the usual stream to boolean type conversion.
This way you can get rid of the while true and explicit break.
std::istream& prompt_for_input( std::istream& in, std::ostream& out, char& response )
{
out << "enter a character at the prompt.\n> ";
in >> response;
return in;
}
int main()
{
char response;
while ( prompt_for_input( std::cin, std::cout, response ) && response != 'q' )
{
wait();
}
}
Upvotes: 3
Reputation: 126488
Upon reading an EOF, you just ignore it and loop back, without exiting the loop, so you'll continually read the EOF and continually loop. If you want to do something on seeing an EOF, you need to handle it either in your switch or before.
Perhaps you want to read input from somewhere after the user has closed your stdin with ^D? In that case, you'll have to close cin and reopen it to read from the other place you want to read input from.
Upvotes: 0
Reputation: 45539
Err, I may be missing something, but I don't ever see you break
out of the while (true)
loop.
// ...
while (true) {
if (std::cin.eof()) {
break;
}
// ...
}
Upvotes: 1
Reputation: 490623
You'll need to clear the flags to get the stream to do much of anything after it encounters EOF.
Upvotes: 0