Update
I found the cause of the problem. I have been experimenting with the fish shell. I saw the comment that said someone had successfully run my code on a Mac, and decided to try it in a standard bash shell. It worked perfectly. So, no more fish shell I guess. :)
I would still appreciate knowing how and why cin works the way it does. This is the main part of my question.
I have run into a popular problem: using cin
in a loop. The code is straightforward, but I cannot find a solution. The answers Google have provided me are varied, but usually involve some combination of cin.clear()
, cin.ignore()
, and cin.get()
. Unfortunately, I haven't been able to find a combination or ordering of those that fix my problem. Moreover, I find it frustrating that I don't have a complete understanding of the function of cin
. I'd rather not use trial and error to fix this. I want to understand exactly what's going on.
What Should Happen
When I run my code, I should see a prompt with a list of options. I should be able to type a character which will run one of the options. Then, it should display the prompt again and repeat the process until I select the Exit option.
What Actually Happens
As soon as I run the code, it prints the prompt to the screen an arbitrary number of times and eventually stops halfway through the prompt. Then I am unable to do anything but kill it with ^C
.
$ ./run
Choose an option:
[A]dd a score
[R]emove a player
[E]xit
: That is not a valid input.
[repeated a bunch of times]
Choose an option:
[A]dd a score
[R]emove a player
[E]xit
: That is not a valid input.
Choose ^C
$
My Question
What causes cin to do that? As an experienced Java developer (but a beginner C++ developer), I'm familiar with the concepts of buffers and streams and such, but I have no idea how cin
works. I know that cin.clear()
clears an error state, and cin.ignore()
ignores a number of characters in the stream. My Google-fu has thus far been unable to find a concise reference.
Why does cin
act the way it does? How should one visualize what happens under the hood when using cin
in a loop? What is the most elegant way to implement this infinite menu idea in C++?
My Code
Here is a simplified version of my code, which produces the exact same problem as the full version:
#include <iostream>
using namespace std;
int main () {
//infinite menu
char input;
while(true) {
//prompt
cout << "\n\nChoose an option:\n";
cout << "[A]dd a score\n";
cout << "[R]emove a player\n";
cout << "[E]xit\n";
cout << "\n\t: ";
//input
cin >> input;
//decide what the input means
switch(input) {
case 'a':
case 'A':
cout << "Add a score.\n";
break;
case 'r':
case 'R':
cout << "Remove a player.\n";
break;
case 'e':
case 'E':
cout << "Program Complete.\n";
return 0;
break;
default:
cout << "That is not a valid input.\n";
}
}
return 0;
}
I compile and run with:
$ g++ Test.cpp -o run
$ ./run
I'm running gcc version 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2336.11.00)
on Mac OS X 10.8.2.
Do yourself a favour and don't extract tokens directly from std::cin
. Instead, read line by line with getline
and then interpret each line. Also, you must always evaluate the result of an input operation in a boolean context, or you will not be able to handle arbitrary input correctly.
For a test, your program must survive if you call echo "abc" | ./run
. This should always be one of your first tests.
Now, on to the code:
#include <string>
#include <iostream>
int main()
{
for (std::string line; std::getline(std::cin, line); )
{
if (line.empty()) { continue; }
if (line == "A" || line == "a") { /* ... */ continue; }
if (line == "R" || line == "r") { /* ... */ continue; }
if (line == "E" || line == "e") { break; }
std::cout << "Sorry, I did not understand.\n";
}
std::cout << "Goodbye!\n";
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With