Weird cin behaviour - c++

After some suggestions. I applied these.
Excerpt from a function bool getInput(int format=1)
cin>>num1>>plus>>num2>>i;
if(cin.fail()){
cout<<"\n[X][cin--fail] Oops! You entered in the incorrect format! Correct format: a+bi";
cin.clear();
cin.ignore(INT_MAX);
return false;
}
if(plus == '+' && i=='i'){
setComplex(num1, num2);
return true;
} else {
cout<<"\n[X] Oops! You entered in the incorrect format! Correct format: a+bi";
return false;
}
I've another function which calls the above mentioned function.
void getInput_recursive(int format=1){
while(!getInput(format)){};
}
It works fine for correct input.
But when the input is like 2+3iiiii then there is some weird thing happens.
My first guess: Maybe this was due to input buffer. I tried to clear it using cin.ignore and cin.clear but still nothing happened.
Can anyone help me out here? What am i doing wrong?
Please find the complete code here: http://codepad.org/Gl2zBrn1

(a) Don't recurse. while (!getInput(format)) {} will do, recursion is for saving state for later resumption and there's nothing to resume here.
(b) You're not returning a value on the false branch
bool getInput_recursive(int format=1){
if(getInput(format) == true){
return true;
} else {
getInput_recursive(format); // here
}
}
and that produces undefined behavior.
and (c) I think you need to look up what
cin.ignore(INT_MAX);
does, and what state that will leave your stream in. That'll do it.

Your approach is good, and you do the right operation but I think you have the order mixed up:
first cin.clear(); to reset the flags, than cin.ignore(INT_MAX); to clear the buffer.
EDIT:
I think you need to replace cin.ignore(INT_MAX); with cin.ignore(INT_MAX,'\n');
Let me know if this does the trick.

Related

How to implement an "ENTER" case into a switch statement

I am working on a class project where I have to create an ordering system for a coffee shop in C++. If it is applicable, I'm working in Visual Studio.
In the project outline, the teacher said that there is a simple integer input to navigate the menu; however, he specifies that if NOTHING is inputted (I'm assuming what I've seen called a "hot enter") that it calculates the receipt and the program resets.
I have tried cin.get() and checking if the buffer is '\n', and this works fine, but my current implementation seems to only be able to capture a hot enter, and fails to roll into the switch case.
For getting input from the user, I've currently tried this:
// Get menu input
if (cin.get() == '\n') { // Check if user hot entered, assign value if so
input = 0;
} else { // If not, do it normally
input = cin.get();
}
However this does not work quite right, and fails to capture inputted integers for use in the switch case. I'm unsure if this sort of implementation is sound in reasoning, or whether there is a much simpler route to have a case for a hot enter.
I don't receive any errors, so I imagine there is something wrong with my understanding of how these functions work, or my implementation is flawed in its logic.
Thank you.
You used cin.get() twice. The second cin.get() in input = cin.get(); is redundant.
// Get menu input
input = cin.get();
if (input == '\n') { // Check if user hot entered, assign value if so
input = 0;
}
//else { // If not, do it normally
// input = cin.get();
// }

Unexpected infinite loop (C++)

while(true)
{
unsigned int option;
cout<<"1 - Display the list\n";
cout<<"2 - Add a game title to the list\n";
cout<<"3 - Remove a game title from the list\n";
cout<<"4 - Exit\n";
cin>>option;
if(option == 1)
{
if(gameTitles.empty())
{
cout<<"\nThere are no games to be displayed. Please try again after adding some games to the list.\n";
}
else
{
for(iter = gameTitles.begin(); iter != gameTitles.end(); ++iter)
{
cout<<*iter<<endl;
}
}
}
else if(option == 2)
{
cout<<"\nEnter the game's title:\n";
cin>>newGame;
gameTitles.push_back("newGame");
}
else if(option == 3)
{
cout<<"\nEnter a game to be removed:\n";
cin>>removeGame;
theIterator = find(gameTitles.begin(),gameTitles.end(),removeGame);
theIterator = gameTitles.erase(theIterator);
}
else if(option == 4)
{
break;
}
else
{
cout<<"\nThe option is illegal. Please try again.\n";
}
}
When I choose any 1, 3, 4 or illegal option, the loop brings me to the top and I have possibility to choose again. The problem arises when I am trying to use 2nd option. I just enter in an infinite loop. But I want to enter a game title and after it is added to my vector (I declared it earlier) and then have possibility to choose an option again.
You don't show the type of newGame but I would guess that it is of type std::string and you enter a title with two words: the stream reads the first word and stops reading. The next thing you do is read an int which fails and leaves the value of option unchanged. From then on the stream won't do anything and just keeps reading.
The key error is not checking that the read attempt was successful before using the result: you always need to verify that your input was successful after reading and before using the result. When an input operation fails the stream goes into failure mode, i.e., std::ios_base::failbit gets set, and it converts to false instead of true (well, actually it converts to a null pointer vs. a non-null pointer but that's a detail irrelevant to this discussion). Once in failure state the stream won't do anything until the stream state is clear()ed. You you probably also need to ignore() the offending characters.
That is, you certainly should use something like
if (std::cin >> option) {
// do something with the option
}
else {
std::cout << "expected an integer: ignoring the line\n";
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
To read the title you should probably read an entire line. Since the formatted input for the option will leave the newline character in the stream, you will first need to skip that character, though. That is, the input for the new title would look something like this:
if (std::getline(std::cin >> std::ws, newGame)) {
// ...
}
The std::ws manipulator skips all leading whitespace. That is probably OK for your needs. If the string being read can have leading whitespace characters something different will be needed.
Before you try to cin again to the same variable newgame, put in a cin.ignore(). If I recall correctly, the first time you cin to a string (I'm assuming newgame is a string), it leaves a trailing \n so it will just automatically enter through in later prompts.

break not terminating do-while loop

So this is going to be a stupid question but I cannot for the life of me figure this out at the moment... This do-while loop below does not terminate as it should.
do {
if(inp.eof())
throw("Unexpected end of file");
getline(inp, l);
line++;
std::stringstream com(l);
com >> command;
if(command == ">>") {
std::cout << "Why?" << std::endl;
break;
}
} while(command != ">>");
From this loop, if command were to ever become ">>", it should end one of two ways right? Unfortunately, my code simply outputs the "Why?" inside the if, ignores the break statement entirely, and continues through the loop until eventually I get to inp.eof(), where it throws an error. This should be basic, but somehow, I have no idea what it's doing.
EDIT: Update: When I change it to a while(true) and keep the break statement, it works perfectly. Change it back to a do-while and it fails. Not sure why, but the darn thing works and that's all I care about at the moment.

Strange behaviour when reading in int from STDIN

Suppose we have a menu which presents the user with some options:
Welcome:
1) Do something
2) Do something else
3) Do something cool
4) Quit
The user can press 1 - 4 and then the enter key. The program performs this operation and then presents the menu back to the user. An invalid option should just display the menu again.
I have the following main() method:
int main()
{
while (true)
switch (menu())
{
case 1:
doSomething();
break;
case 2:
doSomethingElse();
break;
case 3:
doSomethingCool();
break;
case 4:
return 0;
default:
continue;
}
}
and the follwing menu():
int menu()
{
cout << "Welcome:" << endl
<< "1: Do something" << endl
<< "2: Do something else" << endl
<< "3: Do something cool" << endl
<< "4: Quit" << endl;
int result = 0;
scanf("%d", &result);
return result;
}
Entering numerical types works great. Entering 1 - 4 causes the program to perform the desired action, and afterwards the menu is displayed again. Entering a number outside this range such as -1 or 12 will display the menu again as expected.
However, entering something like 'q' will simply cause the menu to display over and over again infinitely, without even stopping to get the user input.
I don't understand how this could possibly be happening. Clearly, menu() is being called as the menu is displayed over and over again, however scanf() is part of menu(), so I don't understand how the program gets into this error state where the user is not prompted for their input.
I originally had cin >> result which did exactly the same thing.
Edit: There appears to be a related question, however the original source code has disappeared from pastebin and one of the answers links to an article which apparently once explained why this is happening, but is now a dead link. Maybe someone can reply with why this is happening rather than linking? :)
Edit: Using this example, here is how I solved the problem:
int getNumericalInput()
{
string input = "";
int result;
while (true)
{
getline(cin, input);
stringstream sStr(input);
if (sStr >> result)
return result;
cout << "Invalid Input. Try again: ";
}
}
and I simply replaced
int result = 0;
scanf("%d", &result);
with
int result = getNumericalInput();
When you try to convert the non-numeric input to a number, it fails and (the important part) leaves that data in the input buffer, so the next time you try to read an int, it's still there waiting, and fails again -- and again, and again, forever.
There are two basic ways to avoid this. The one I prefer is to read a string of data, then convert from that to a number and take the appropriate action. Typically you'll use std::getline to read all the data up to the new-line, and then attempt to convert it. Since it will read whatever data is waiting, you'll never get junk "stuck" in the input.
The alternative is (especially if a conversion fails) to use std::ignore to read data from the input up to (typically) the next new-line.
1) Say this to yourself 1000 times, or until you fall asleep:
I will never ever ever use I/O functions without checking the return value.
2) Repeat the above 50 times.
3) Re-read your code: Are you checking the result of scanf? What happens when scanf cannot convert the input into the desired format? How would you go about learning such information if you didn't know it? (Four letters come to mind.)
I would also question why you'd use scanf rather than the more appropriate iostreams operation, but that would suffer from exactly the same problem.
You need to verify if the read succeeded. Hint: it did not. Always test after reading that you successfully read the input:
if (std::cin >> result) { ... }
if (scanf("%d", result) == 1) { ... }
In C++ the failed state is sticky and stays around until it gets clear()ed. As long as the stream is in failed state it won't do anything useful. In either case, you want to ignore() the bad character or fgetc() it. Note, that failure may be due to having reached the end of the stream in which case eof() is set or EOF is returned for iostream or stdio, respectively.

Incorrect logic flow? function that gets coordinates for a sudoku game

This function of mine keeps on failing an autograder, I am trying to figure out if there is a problem with its logic flow? Any thoughts?
Basically, if the row is wrong, "invalid row" should be printed, and clearInput(); called, and return false. When y is wrong, "invalid column" printed, and clearInput(); called and return false.
When both are wrong, only "invalid row" is to be printed (and still clearInput and return false.
Obviously when row and y are correct, print no error and return true.
My function gets through most of the test cases, but fails towards the end, I'm a little lost as to why.
bool getCoords(int & x, int & y)
{
char row;
bool noError=true;
cin>>row>>y;
row=toupper(row);
if(row>='A' && row<='I' && isalpha(row) && y>=1 && y<=9)
{
x=row-'A';
y=y-1;
return true;
}
else if(!(row>='A' && row<='I'))
{
cout<<"Invalid row"<<endl;
noError=false;
clearInput();
return false;
}
else
{
if(noError)
{
cout<<"Invalid column"<<endl;
}
clearInput();
return false;
}
}
It's hard to know without seeing the input, but here's a couple of possible issues:
(1) The way you detect that the column read failed is by examining the value of y - but are you sure that it's set to a value outside the range 1-9 by the calling code? Otherwise even if the read fails you might think it succeeds. You could fix this by adding a y=0; to the beginning of the function.
Some more explanation: when you use cin >> y to read an integer into y but there isn't an integer to read (could be because there's a non-digit in the stream, or EOF is reached, or whatever) the value of y won't be touched. So, imagine your input looks like "CB". Then after you cin >> row >> y, row contains 'C' and y still contains whatever value it had before the function was called (remember y is a reference to a variable outside this function!) This is the problem: you check to see if the read succeeded by looking at the value of y, but it's quite possible that it is between 1 and 9 even if the read failed (especially if you're calling this function in a loop and reusing the same y). So your code thinks everything went OK, even though it didn't and the stream has now got its failbit set... it's not surprising if everything goes a bit crazy after this point.
(2) The way you're reading data using extraction from cin could misbehave if a line is incomplete. That's because the reading of an integer will skip leading whitespace - including newlines. So if the (invalid) input line is just "B", for example, you'd read the "B" correctly by extracting a char, and then the integer extraction would swallow the newline and fail (assuming the next input line doesn't start with an integer). Then your ignore would completely skip the next line (unless you don't pick up on the failure because of point (1)!).