Home > Enterprise >  C Switch Case with Do While Loop
C Switch Case with Do While Loop

Time:09-08

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, and

  • check 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;

  • Related