I am attempting to learn how to make a menu that loops if invalid input is entered.
I am trying to have the loop control determine if the loop should quit or looping or not.
If I do an invalid selection like "k", it continually loops without prompting me for input.
int num = 0;
bool isValid = true;
do {
num = 0;
cout << endl << "---MENU---\n" << endl;
cout << "1- Insert\n";
cout << "2- remove\n";
cout << "3- SeqSearch\n";
cout << "4- PrintList\n";
cout << "5- Quit\n" << endl;
cout << "Selection> ";
cin >> num;
cin.clear();
cin.ignore();
switch (num) {
case 1:
num = 1;
cout << "Insert\n";
break;
case 2:
num = 2;
cout << "remove\n";
break;
case 3:
num = 3;
cout << "SeqSearch\n";
break;
case 4:
num = 4;
cout << "PrintList\n";
break;
case 5:
num = 5;
cout << "Exiting the program.\n" << endl;
exit(0);
default:
cout << "Invalid Selection\n";
isValid = false;
break;
}
} while (!isValid);
CodePudding user response:
Before using the result of cin >> num;
, you should test whether the input conversion was successful or not. One simply way to do this would be to use cin.operator bool
.
std::cout << "Selection> ";
std::cin >> num;
//test for conversion failure
if ( !std::cin )
{
std::cout << "Unable to convert input to integer!\n";
//clear fail flag
std::cin.clear();
//discard remainder of line from input stream
std::cin.ignore( std::numeric_limits<std::streamsize>::max(), '\n' );
continue;
}
//input was successful, so we can now use the result
switch ( num )
{
[...]
}
It is important to note that I am discarding the remainder of the line after every attempt to read an integer from the input stream. If I did not do this, then if the user entered k
, then the k
would stay on the input stream, causing the conversion to fail again in the next loop iteration.
Note that you will have to #include <limits>
in order to use std::numeric_limits
.
However, this solution still is not perfect, because if the user enters for example 6k
, then the conversion of the 6
will be successful in the current loop iteration, but it will leave k
on the input stream, so that in the next loop iteration, the program will attempt to convert that k
to an integer, instead of waiting for the user to enter more input. For this reason, it would probably be better to
discard the remainder of the line in all cases, even in cases in which
std::cin >> num;
is successful, andcheck the remainder of the line for non-whitespace characters, and treat the entire line as invalid if such a character is found, even if
std::cin >> num;
is successful.
Here is the full code solution which also fixes the issues mentioned above:
#include <iostream>
#include <limits>
#include <cctype>
int main()
{
for (;;) //infinite loop, equivalent to while(true)
{
int num;
char c;
std::cout <<
"\n"
"---MENU---\n"
"\n"
"1- Insert\n"
"2- remove\n"
"3- SeqSearch\n"
"4- PrintList\n"
"5- Quit\n"
"\n";
std::cout << "Selection> ";
std::cin >> num;
//test for conversion failure
if ( !std::cin )
{
std::cout << "Unable to convert input to integer!\n";
//clear fail flag
std::cin.clear();
//discard remainder of line from input stream
std::cin.ignore( std::numeric_limits<std::streamsize>::max(), '\n' );
continue;
}
//verify that remainder of line is either empty or
//contains harmless whitespace characters, and
//discard this remainder
while ( std::cin.get( c ) && c != '\n' )
{
if ( !std::isspace( static_cast<unsigned char>( c ) ) )
{
std::cout << "Invalid character found!\n";
//discard remainder of line
std::cin.ignore( std::numeric_limits<std::streamsize>::max(), '\n' );
//we cannot use "continue" here, because that would
//go to the next iteration of the innermost loop, but
//we cant to go the next iteration of the outer loop
goto continue_outer_loop;
}
}
//input was successful, so we can now use the result
switch ( num )
{
case 1:
num = 1;
std::cout << "Insert\n";
break;
case 2:
num = 2;
std::cout << "remove\n";
break;
case 3:
num = 3;
std::cout << "SeqSearch\n";
break;
case 4:
num = 4;
std::cout << "PrintList\n";
break;
case 5:
num = 5;
std::cout << "Exiting the program.\n\n";
return EXIT_SUCCESS;
default:
std::cout << "Invalid Selection\n";
}
continue_outer_loop:
continue;
}
}
Note that this program uses one goto
statement. Normally, goto
should be avoided, but for breaking out of a nested loop, it is acceptable.
This program has the following behavior:
---MENU---
1- Insert
2- remove
3- SeqSearch
4- PrintList
5- Quit
Selection> k
Unable to convert input to integer!
---MENU---
1- Insert
2- remove
3- SeqSearch
4- PrintList
5- Quit
Selection> 6k
Invalid character found!
---MENU---
1- Insert
2- remove
3- SeqSearch
4- PrintList
5- Quit
Selection> 0
Invalid Selection
---MENU---
1- Insert
2- remove
3- SeqSearch
4- PrintList
5- Quit
Selection> 1
Insert
---MENU---
1- Insert
2- remove
3- SeqSearch
4- PrintList
5- Quit
Selection> 2
remove
---MENU---
1- Insert
2- remove
3- SeqSearch
4- PrintList
5- Quit
Selection> 3
SeqSearch
---MENU---
1- Insert
2- remove
3- SeqSearch
4- PrintList
5- Quit
Selection> 4
PrintList
---MENU---
1- Insert
2- remove
3- SeqSearch
4- PrintList
5- Quit
Selection> 5
Exiting the program.
CodePudding user response:
You should add
cin.clear();
cin.ignore();
after cin >> num;