Search code examples
c++switch-statementdo-while

Receiving unnecessary "cout" outputs in terminal menu


The below code serves as a simple terminal window menu which I will later build into a larger application. Letters correspond to an operation the user would like to select and to do so the user enters the singular corresponding letter. Illegitimate responses are met with an error. Typing nothing and pressing Enter prints an arrow on the next line and awaits valid user input. The menu currently serves its desired purpose, though, it's unpolished in its current presentation.

#include <iostream>

int main()
{
    std::cout << "Operations:\n" << "\ta)\n" << "\tb)\n" << "\tc)\n\n";

    char choice;
    bool check;
    
    do{
        std::cout << "> ";
        std::cin >> std::noskipws >> choice;
        switch (choice) {
            case 'a':
            case 'A':
            {
                std::cout << "A worked!\n";
                check = false;
                break;
            }
            case 'b':
            case 'B':
            {
                std::cout << "B worked!\n";
                check = false;
                break;
            }
            case 'c':
            case 'C':
            {
                std::cout << "C worked!\n";
                check = false;
                break;
            }
            case 32:
            case 9:
            case 10:
            case 13:
            {
                check = true;
                break;
            }
            default:
            {
                std::cout << "\nERROR. Select an operation from the above menu by typing its corresponding letter\n";
                check = true;
            }
        }
    }while(check == true);
return 0;
}

I intended for the newline/return to function similar to a regular terminal window where the prompt (in my case, just "> ") follows your cursor and if no characters which constitute whitespace are typed in, no error is returned. Currently, only newline/return alone yields just that, but every additional character input now increases the output messages correspondingly.

For example, the following inputs output this:

  1. Input: (Enter)

    Output: > (This is good)

  2. Input: (space)(Enter)

    Output: >> (This is not good)

  3. Input: q(Enter)

    Output: ERROR. Select an operation from the above menu by typing its corresponding letter.

    (>>) (This outputs two lines as intended, but because there are two characters being inputted, it also outputs 2 arrows on the next line)

  4. Input: qq(Enter)

    Output: ERROR. Select an operation from the above menu by typing its corresponding letter.

    (>)

    ERROR. Select an operation from the above menu by typing its corresponding letter.

    (>>)

The only solution that comes to mind is to change "char choice" into a string instead, so that I can limit the length of each input to one character. However, I'm aware that strings are not a data-type in C++ and cannot be used in switches. Do I need to print as string, but convert the value to char for use in the switch? Even then I don't know if that would prevent the dual input caused by inputting a letter and the subsequent newline/return.

What are my options here?


Solution

  • In C++ IO and especially IO error checking is notoriously difficult.

    I would recommend to implement a small function.

    In this function you may run a loop, until you have received exactly one alpha letter. For that you may use the save std::getline function and then check the content of a complete line.

    • If the string is empty, then just "Enter" was pressed.
    • If the string contains exactly one alpha letter, then everything is OK
    • Other characters, like "digits" or longer strings will be rejected.

    At the end of the function we can convert the alpha character to always return a lowercase letter.

    This makes life easier int the switch statement. No need to check for 'A' and 'a'.

    In "main", you can call simply your function, to always get a correct letter.

    Please see the following example code:

    #include <iostream>
    #include <string>
    #include <cctype>
    
    char getLowerCaseLetter() {
    
        // The letter that we will return
        char letter{};
    
        // Rund loop as long as we do not have a vild letter
        bool validLetter{};
        while (not validLetter) {
    
            // Read a complete line and skip all leading spaces
            std::string line{};
            if (std::getline(std::cin, line)) {
    
                // Just "enter" will do nothing
                if (line.empty()) {
                    std::cout << "> ";
                }
                else {
                    // If we found one alpha letter, then OK
                    if (line.length() == 1 and std::isalpha(line.at(0))) {
                        validLetter = true;
                        letter = line.at(0);
                    }
                    else {
                        std::cout << "\nInvalid Input. Please enter 1 alpha letter and press Enter\n\n> ";
                    }
                }
            }
            else std::cerr << "\n***Unexpected IO Error\n";
        } // Always lower case
        return std::tolower(letter);
    }
    
    int main() {
        bool runProgram{ true };
        while (runProgram) {
    
            // Give information to user
            std::cout << "\n\n\nOperations:\n\ta)\n\tb)\n\tc)\n\tx) to quit\n\n> ";
    
            // Get users choice
            switch (getLowerCaseLetter()) {
            case 'a':
                std::cout << "\nEntered 'a'\n";
                break;
            case 'b':
                std::cout << "\nEntered 'b'\n";
                break;
            case 'c':
                std::cout << "\nEntered 'c'\n";
                break;
            case 'x':
                std::cout << "\nEntered 'x'. Leaving program . . .\n";
                runProgram = false;
                break;
            default:
                std::cout << "\nInvalid selection, Please try again\n";
                break;
            }
        }
    }