ZeeZeeZee
ZeeZeeZee

Reputation: 73

How do I prevent a menu in switch function from repeating itself?

In this code the menu will appear twice after a section of a or b is made, how do i prevent that from happening? I suspect that it has something to do with cin.get capturing a space perhaps from the first cout statement. If that's the case then the solution would be something that will consume that space. I know getline works for strings but this is a char data type. Also a quick related question, is there a piece of code that acts like getline(cin << ws, stringName) does for strings but for char data types?

#include <iostream>
#include <string>
#include <cctype>

using namespace std;


void displayMenu();

int main()
{
    char cSelection;
    string sSelection;

    //Create menu
    const char OPTION_READING = 'A', OPTION_READINGTWO = 'B', OPTION_ENDING = 'C';

    do
    {
        displayMenu();
        cout << "\nPlease choose an option" << endl << endl;
        cin.get(cSelection);

        //respond to choice
        switch (cSelection)
        {
        case OPTION_READING:
        case 'a':
            cout << "You picked 1" << endl;
            break;

        case OPTION_READINGTWO:
        case 'b':
            cout << "You picked 2" << endl;
            break;

        case OPTION_ENDING:
        case 'c':
            cout << "Thank you for using this program!";
            return 0;

        default:
            cout << cSelection << " " << "is an invalid choice";


        }
    } while (toupper(sSelection[0] != OPTION_ENDING));


}
void displayMenu()
{

    cout << "\t \t \t" "Menu" << endl << endl;
    cout << "A. Option 1\n";
    cout << "B. Option 2\n";
    cout << "C. Quit program";

}

Upvotes: 0

Views: 1167

Answers (3)

WhozCraig
WhozCraig

Reputation: 66234

Your additional menu hit is from consumption of a newline char that is still in the input stream. Consume it any number of ways, one of which is shown below. I also took the liberty to fix your while condition, which was not correctly expressed (you were passing a bool to toupper, which was clearly not what you wanted. Likewise I changed the use of sSelection[0], which is not initialized in this code to cSelection. As it was, you would invoke undefined behavior:

#include <iostream>
#include <string>
#include <cctype>
#include <limits>
using namespace std;

void displayMenu();

int main()
{
    int cSelection;
    string sSelection;

    //Create menu
    const char OPTION_READING = 'A', OPTION_READINGTWO = 'B', OPTION_ENDING = 'C';

    do
    {
        displayMenu();
        cout << "\nPlease choose an option" << endl << endl;
        if ((cSelection = cin.get()) == EOF)
            break;

        // eat newline character
        cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');

        //respond to choice
        switch (cSelection)
        {
            case OPTION_READING:
            case 'a':
                cout << "You picked 1" << endl;
                break;

            case OPTION_READINGTWO:
            case 'b':
                cout << "You picked 2" << endl;
                break;

            case OPTION_ENDING:
            case 'c':
                cout << "Thank you for using this program!\n";
                return 0;

            default:
                cout << cSelection << " " << "is an invalid choice";


        }
    } while (std::toupper(static_cast<unsigned char>(cSelection)) != OPTION_ENDING);


}

void displayMenu()
{
    cout << "\t \t \t" "Menu" << endl << endl;
    cout << "A. Option 1\n";
    cout << "B. Option 2\n";
    cout << "C. Quit program";
}

How It Works

That cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); may look a little cryptic, but it really isn't that bad. C++11 provides a very nice template class called std::numeric_limits. Given a numeric type, you can fire a static member function of the class and get the numeric limits (min, max, etc) of the type. There are tons of of specializations of that template for every native language type. I advise following the link I provided and checking out how it works and all that it offers.

In our case we're interested in consuming as many chars as possible until we reach a newline, which will be discarded. We use the ignore() method from our input stream, std::cin, to accomplish the skipping. Therefore:

cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');

effectively says "Skip as many chars as you have to looking for a newline. If you find a newline, skip it, then return. Consume as many as you have to, up to EOF if needed."

It may look a little cryptic, but its not uncommon at all, and quite effective.

Anyway, hope it helps.

Upvotes: 1

Jarod42
Jarod42

Reputation: 217870

with #include <limits>

after

cin.get(cSelection);

you may add:

std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');

Upvotes: 1

robbannn
robbannn

Reputation: 5013

Place cin.get(); after cin.get(cSelection); to clear newline.

Upvotes: 1

Related Questions