For loop and nested do while loop not working as intended

102 views Asked by At

I am trying to create a menu driven program, with a for loop and nested do while loop, however, the for loop is repeating infinitely and the do while loop is not repeating even when conditions are met. I have tried using if else statements for the subChoices instead of switch, but that did not change anything in the output. I don't seen anything wrong with the logic, so guessing I have messed up the loop somehow else. I want the loop to repeat for each user until they are done by entering 4, and then repeat for other users up to a max of 3.

int main() {
// Variable declarations
string lineOfStars(60, '*');
int numA1 = 0, numA2 = 0, numA3 = 0;
int numD1 = 0, numD2 = 0, numD3 = 0;
int numM1 = 0, numM2 = 0, numM3 = 0, numM4 = 0;
double total = 0;
string name;
int numPeople;
int mainChoice;
int subChoice;

// Print header
cout << endl << lineOfStars << endl;
cout << setw(40) << "HOGWARTS RESTAURANT";
cout << endl << lineOfStars << endl;

// Start user input
cout << "How many people are included in your order? ";
cin >> numPeople;

// Input validation for number of people
while(numPeople < 1 || numPeople > 3) {
    cout << "Oops! Please select 1, 2, or 3." << endl;
    cout << "How many people are included in your order? ";
    cin >> numPeople;
}

// Outer loop for each person
for(int i = 1; i <= numPeople; i++) {
    // Start of menu selection loop for each person
    do {
        cout << "Person " << i << ", what is your name? ";
        cin.ignore();
        getline(cin, name);
        cout << "\nHi, " << name << ". What would you like to order?" << endl;
        cout << "1. Appetizer\n2. Drinks\n3. Meal\n4. I'm done!\n";
        cout << "Enter 1-4: ";
        cin >> mainChoice;

        // Input validation for main menu choice
        while(mainChoice < 1 || mainChoice > 4) {
            cout << "Oops! That isn't a valid choice." << endl;
            cout << "Enter 1-4: ";
            cin >> mainChoice;
        }

        // Process user's main menu choice
        switch(mainChoice) {
            case 1: // Appetizer
                cout << "\nGreat, you have selected Appetizer!" << endl;
                cout << "Choose from the following appetizers:" << endl;
                cout << "1. Dumbledonuts\t$2.99\n2. Potterfries\t$5.99\n3. Griffintoes\t$4.99\n";
                cout << "Choose 1-3: ";
                cin >> subChoice;

                // Input validation for appetizer choice
                while(subChoice < 1 || subChoice > 3) {
                    cout << "Oops! That isn't a valid choice." << endl;
                    cout << "Choose 1-3: ";
                    cin >> subChoice;
                }

                // Process user's appetizer choice and update total
                switch(subChoice) {
                    case 1:
                        cout << "How many Dumbledonuts do you want? ";
                        cin >> numA1;
                        total += (numA1 * 2.99);
                        break;
                    case 2:
                        cout << "How many Potterfries do you want? ";
                        cin >> numA2;
                        total += (numA2 * 5.99);
                        break;
                    case 3:
                        cout << "How many Griffintoes do you want? ";
                        cin >> numA3;
                        total += (numA3 * 4.99);
                        break;
                }
                break;
            case 2: // Drinks
                cout << "\nGreat, you have selected Drinks!" << endl;
                cout << "Choose from the following drinks:" << endl;
                cout << "1. Polyjuice Potion\t$5.99\n2. Death Eater Negroni\t$6.99\n3. Butter Beer\t$4.99\n";
                cout << "Choose 1-3: ";
                cin >> subChoice;

                // Input validation for drink choice
                while(subChoice < 1 || subChoice > 3) {
                    cout << "Oops! That isn't a valid choice." << endl;
                    cout << "Choose 1-3: ";
                    cin >> subChoice;
                }

                // Process user's drink choice and update total
                switch(subChoice) {
                    case 1:
                        cout << "How many Polyjuice Potions do you want? ";
                        cin >> numD1;
                        total += (numD1 * 5.99);
                        break;
                    case 2:
                        cout << "How many Death Eater Negronis do you want? ";
                        cin >> numD2;
                        total += (numD2 * 6.99);
                        break;
                    case 3:
                        cout << "How many Butter Beers do you want? ";
                        cin >> numD3;
                        total += (numD3 * 4.99);
                        break;
                }
                break;
            case 3: // Meal
                cout << "\nGreat, you have selected Meal!" << endl;
                cout << "Choose from the following meals:" << endl;
                cout << "1. Griffinwings (10pcs)\t$10.99\n2. Voldenose\t$14.99\n3. Hufflemuffin\t$8.99\n4. Ravenclaw Meat\t$19.99\n";
                cout << "Choose 1-4: ";
                cin >> subChoice;

                // Input validation for meal choice
                while(subChoice < 1 || subChoice > 4) {
                    cout << "Oops! That isn't a valid choice." << endl;
                    cout << "Choose 1-4: ";
                    cin >> subChoice;
                }

                // Process user's meal choice and update total
                switch(subChoice) {
                    case 1:
                        cout << "How many Griffinwings (10pcs) do you want? ";
                        cin >> numM1;
                        total += (numM1 * 10.99);
                        break;
                    case 2:
                        cout << "How many Voldenoses do you want? ";
                        cin >> numM2;
                        total += (numM2 * 14.99);
                        break;
                    case 3:
                        cout << "How many Hufflemuffins do you want? ";
                        cin >> numM3;
                        total += (numM3 * 8.99);
                        break;
                    case 4:
                        cout << "How many Ravenclaw Meats do you want? ";
                        cin >> numM4;
                        total += (numM4 * 19.99);
                        break;
                }
                break;
        }
    } while(mainChoice != 4); // End of do-while loop for each person
}
2

There are 2 answers

0
mmj On

For submenus you had not added the do while loop. for loop terminates correctly. Here is the working code. The program works fine now.

#include <iostream>
using namespace std;
int main() {
// Variable declarations
string lineOfStars(60, '*');
int numA1 = 0, numA2 = 0, numA3 = 0;
int numD1 = 0, numD2 = 0, numD3 = 0;
int numM1 = 0, numM2 = 0, numM3 = 0, numM4 = 0;
double total = 0;
string name;
int numPeople;
int mainChoice;
int subChoice;

// Print header
cout << endl << lineOfStars << endl;
cout << "HOGWARTS RESTAURANT";
cout << endl << lineOfStars << endl;

// Start user input
cout << "How many people are included in your order? ";
cin >> numPeople;

// Input validation for number of people
while(numPeople < 1 || numPeople > 3) {
    cout << "Oops! Please select 1, 2, or 3." << endl;
    cout << "How many people are included in your order? ";
    cin >> numPeople;
}

// Outer loop for each person
for(int i = 1; i <= numPeople; i++) {
        cout << "Person " << i << ", what is your name? ";
        cin.ignore();
        getline(cin, name);
    // Start of menu selection loop for each person
    do {
        cout << "\nHi, " << name << ". What would you like to order?" << endl;
        cout << "1. Appetizer\n2. Drinks\n3. Meal\n4. I'm done!\n";
        cout << "Enter 1-4: ";
        cin >> mainChoice;

        // Input validation for main menu choice
        while(mainChoice < 1 || mainChoice > 4) {
            cout << "Oops! That isn't a valid choice." << endl;
            cout << "Enter 1-4: ";
            cin >> mainChoice;
        }

        // Process user's main menu choice
        switch(mainChoice) {
            case 1: // Appetizer
                cout << "\nGreat, you have selected Appetizer!" << endl;
                do{
                cout << " from the following appetizers:" << endl;
                cout << "1. Dumbledonuts\t$2.99\n2. Potterfries\t$5.99\n3. Griffintoes\t$4.99\n4. ExitMenu\n";
                cout << "Choose 1-4: ";
                cin >> subChoice;

                // Input validation for appetizer choice
                while(subChoice < 1 || subChoice > 4) {
                    cout << "Oops! That isn't a valid choice." << endl;
                    cout << "Choose 1-4: ";
                    cin >> subChoice;
                }

                // Process user's appetizer choice and update total
                switch(subChoice) {
                    case 1:
                        cout << "How many Dumbledonuts do you want? ";
                        cin >> numA1;
                        total += (numA1 * 2.99);
                        break;
                    case 2:
                        cout << "How many Potterfries do you want? ";
                        cin >> numA2;
                        total += (numA2 * 5.99);
                        break;
                    case 3:
                        cout << "How many Griffintoes do you want? ";
                        cin >> numA3;
                        total += (numA3 * 4.99);
                        break;
                }
                }while(subChoice != 4);
                break;
            case 2: // Drinks
                cout << "\nGreat, you have selected Drinks!" << endl;
                do{
                cout << "Choose from the following drinks:" << endl;
                cout << "1. Polyjuice Potion\t$5.99\n2. Death Eater Negroni\t$6.99\n3. Butter Beer\t$4.99\n4. ExitMenu\n";
                cout << "Choose 1-4: ";
                cin >> subChoice;

                // Input validation for drink choice
                while(subChoice < 1 || subChoice > 4) {
                    cout << "Oops! That isn't a valid choice." << endl;
                    cout << "Choose 1-3: ";
                    cin >> subChoice;
                }

                // Process user's drink choice and update total
                switch(subChoice) {
                    case 1:
                        cout << "How many Polyjuice Potions do you want? ";
                        cin >> numD1;
                        total += (numD1 * 5.99);
                        break;
                    case 2:
                        cout << "How many Death Eater Negronis do you want? ";
                        cin >> numD2;
                        total += (numD2 * 6.99);
                        break;
                    case 3:
                        cout << "How many Butter Beers do you want? ";
                        cin >> numD3;
                        total += (numD3 * 4.99);
                        break;
                }
                }while(subChoice!=4);
                break;
            case 3: // Meal
                cout << "\nGreat, you have selected Meal!" << endl;
                do{
                cout << "Choose from the following meals:" << endl;
                cout << "1. Griffinwings (10pcs)\t$10.99\n2. Voldenose\t$14.99\n3. Hufflemuffin\t$8.99\n4. Ravenclaw Meat\t$19.99\n5. ExitMenu\n";
                cout << "Choose 1-5: ";
                cin >> subChoice;

                // Input validation for meal choice
                while(subChoice < 1 || subChoice > 5) {
                    cout << "Oops! That isn't a valid choice." << endl;
                    cout << "Choose 1-5: ";
                    cin >> subChoice;
                }

                // Process user's meal choice and update total
                switch(subChoice) {
                    case 1:
                        cout << "How many Griffinwings (10pcs) do you want? ";
                        cin >> numM1;
                        total += (numM1 * 10.99);
                        break;
                    case 2:
                        cout << "How many Voldenoses do you want? ";
                        cin >> numM2;
                        total += (numM2 * 14.99);
                        break;
                    case 3:
                        cout << "How many Hufflemuffins do you want? ";
                        cin >> numM3;
                        total += (numM3 * 8.99);
                        break;
                    case 4:
                        cout << "How many Ravenclaw Meats do you want? ";
                        cin >> numM4;
                        total += (numM4 * 19.99);
                        break;
                }
                }while(subChoice !=5);
                break;
        }
    } while(mainChoice != 4); // End of do-while loop for each person
}
}
0
tbxfreeware On

I am trying to create a menu driven program, with a for loop and nested do while loop, however, the for loop is repeating infinitely and the do while loop is not repeating even when conditions are met.

You are the victim of illusion! Your program runs as intended, except it prompts too often for the name of a patron. Every time the main menu is redisplayed, you ask again for the customer's name. This creates the illusion that your outer loop is not working, and that you are stuck on a customer.

    // Outer loop for each person
    for (int i = 1; i <= numPeople; i++) {
        // Start of menu selection loop for each person
        do {
            cout << "Person " << i << ", what is your name? ";  // MOVE THIS STATEMENT
            cin.ignore();                                       //  ... AND THIS
            getline(cin, name);                                 //  ... AND THIS
            cout << "\nHi, " << name << ". What would you like to order?" << endl;

            // ...

The marked statements should appear before the do-while loop begins.


    // Outer loop for each person
    for (int i = 1; i <= numPeople; i++) {
        cout << "Person " << i << ", what is your name? ";
        cin.ignore();
        getline(cin, name);

        // Start of menu selection loop for each person
        do {
            cout << "\nHi, " << name << ". What would you like to order?" << endl;

            // ...

Output from a sample run

With just that correction I got the following output during a sample run.

************************************************************
                     HOGWARTS RESTAURANT
************************************************************
How many people are included in your order? 2
Person 1, what is your name? Miley Cyrus

Hi, Miley Cyrus. What would you like to order?
1. Appetizer
2. Drinks
3. Meal
4. I'm done!
Enter 1-4: 1

Great, you have selected Appetizer!
Choose from the following appetizers:
1. Dumbledonuts $2.99
2. Potterfries  $5.99
3. Griffintoes  $4.99
Choose 1-3: 2
How many Potterfries do you want? 3

Hi, Miley Cyrus. What would you like to order?
1. Appetizer
2. Drinks
3. Meal
4. I'm done!
Enter 1-4: 2

Great, you have selected Drinks!
Choose from the following drinks:
1. Polyjuice Potion     $5.99
2. Death Eater Negroni  $6.99
3. Butter Beer  $4.99
Choose 1-3: 2
How many Death Eater Negronis do you want? 1

Hi, Miley Cyrus. What would you like to order?
1. Appetizer
2. Drinks
3. Meal
4. I'm done!
Enter 1-4: 4
Person 2, what is your name? Olivia Rodrigo

Hi, Olivia Rodrigo. What would you like to order?
1. Appetizer
2. Drinks
3. Meal
4. I'm done!
Enter 1-4: 2

Great, you have selected Drinks!
Choose from the following drinks:
1. Polyjuice Potion     $5.99
2. Death Eater Negroni  $6.99
3. Butter Beer  $4.99
Choose 1-3: 1
How many Polyjuice Potions do you want? 2

Hi, Olivia Rodrigo. What would you like to order?
1. Appetizer
2. Drinks
3. Meal
4. I'm done!
Enter 1-4: 3

Great, you have selected Meal!
Choose from the following meals:
1. Griffinwings (10pcs) $10.99
2. Voldenose    $14.99
3. Hufflemuffin $8.99
4. Ravenclaw Meat       $19.99
Choose 1-4: 4
How many Ravenclaw Meats do you want? 1

Hi, Olivia Rodrigo. What would you like to order?
1. Appetizer
2. Drinks
3. Meal
4. I'm done!
Enter 1-4: 4

Call a function to input numbers from the keyboard

One easy way to get valid input from std::cin, is to write a function that handles both input and validation. Function get_integer (below) is such a function. It prompts the user to enter an integer, and keeps looping until a valid entry is made, which is then returned to the caller.

Optional arguments for min and max allow the caller to specify the range for valid entries.

  • When parameter min is omitted, it defaults to std::numeric_limits<int>::min(), which effectively means there is no lower limit.
  • Similarly, when parameter max is omitted, it defaults to std::numeric_limits<int>::max(), which means there is no upper limit.
  • When an entry is out of range, a carefully crafted error message mentions min and max only when they are different from their default values.
  • Parameters ist and ost, respectively, default to std::cin and std::cout. It is useful to have them as parameters in case you want to use other streams during testing.

This version is based on std::getline, so you can mix it with other calls to getline without needing to call cin.ignore. As an exercise, you could modify it so that is uses the stream extraction operator >>, rather than getline. I have a version that does that. It is useful when a line contains more than one value, and you just want to read the first.

int get_integer(
    std::string const& prompt,
    int const min = std::numeric_limits<int>::min(),
    int const max = std::numeric_limits<int>::max(),
    std::istream& ist = std::cin,
    std::ostream& ost = std::cout)
{
    int n;
    for (;;)
    {
        ost << prompt;
        std::string s;
        if (!std::getline(ist, s))
            throw std::runtime_error("get_integer: `std::getline` failed unexpectedly.");
        std::istringstream iss{ s };
        if ((iss >> n) && !iss.eof())
            iss >> std::ws;
        if (iss.fail() || !iss.eof())
        {
            ost << "Invalid entry: " << std::quoted(s) << ". Please reenter.\n\n";
        }
        else if (n < min || n > max)
        {
            ost << std::format("Invalid entry: {}. Please reenter.\n", n);
            if (min > std::numeric_limits<int>::min() &&
                max < std::numeric_limits<int>::max())
                ost << std::format("Entries must be integers between {} and {}.\n\n", min, max);
            else if (min > std::numeric_limits<int>::min())
                ost << std::format("Entries must not be less than {}.\n\n", min);
            else if (max < std::numeric_limits<int>::max())
                ost << std::format("Entries must not be greater than {}.\n\n", max);
        }
        else
        {
            break;
        }
    }
    return n;
}

A bit of top-down design

This program would be easier to code if you had more functions. Twenty-five lines for a function is okay. Fifty is beginning to be too many. Most functions (but not necessarily all) should be shorter. In many classes, I write functions that have only one or two lines.

In his "Better Code" series of talks, the great Sean Parent stresses repeatedly that the correct number of loops in most functions is either zero or one. Anything more, and you should consider refactoring.

After banging about a bit, I came up with this version of function main:

int main()
{
    std::cout <<
        "\n**********************************"
        "\n       HOGWARTS RESTAURANT"
        "\n**********************************"
        "\n"
        "\n Welcome to Hogwarts."
        "\n\n";
    int numPeople = get_integer(" How many people are included in your order? ", 1, 3);
    std::cout << '\n';
    double total = 0.0;
    for (int i = 1; i <= numPeople; ++i)
        total += take_order(i);
    std::cout << std::format(" Total for all orders: $ {:.2f}", total) << "\n\n";
    return 0;
}

The nice thing about this is that you can write a stub function for take_order, and then compile/test function main.

double take_order(int const num) {
    return 0.0;
}

Here is the output:

**********************************
       HOGWARTS RESTAURANT
**********************************

 Welcome to Hogwarts.

 How many people are included in your order? 1

 Total for all orders: $ 0.00

Each menu item is a function call

The next stage in my top-down design is to fill out stub function take_order. It displays a menu, calls function get_integer to input the user's choice, and then calls stub functions to carry out the commands.

double take_order(int const num)
{
    std::cout << " Person " << num << ", what is your name? ";
    std::string person;
    std::getline(std::cin, person);
    std::cout << "\n Hi, " << person << ". What would you like to order?" << '\n';
    char const* const menu =
        "\n**********************************"
        "\n            Order Menu"
        "\n**********************************"
        "\n 1. Appetizer"
        "\n 2. Drinks"
        "\n 3. Meal"
        "\n 4. I'm done!"
        "\n"
        "\n";
    enum : int { Appetizer = 1, Drinks, Meal, Done };

    double total_appetizers = 0.0;
    double total_drinks = 0.0;
    double total_meals = 0.0;
    double total = 0.0;

    int choice = Done;
    do {
        std::cout << menu;
        choice = get_integer(" Choice? ", 1, 4);
        std::cout << '\n';
        double order = 0.0;
        switch (choice)
        {
        case Appetizer:
            order = appetizer_order();  // Stub function returns 0.0
            total_appetizers += order;
            total += order;
            break;
        case Drinks:
            order = drink_order();      // Same with this one...
            total_drinks += order;
            total += order;
            break;
        case Meal:
            order = meal_order();       // And this...    
            total_meals += order;
            total += order;
            break;
        case Done:
            break;
        default:
            std::unreachable();
        }
    } while (choice != Done);
    display_order(                      // This stub does absolutely nothing.
        person, 
        total_appetizers, 
        total_drinks, 
        total_meals, 
        total);
    return total;
}

std::unreachable() is a C++23 feature that allows you to identify to the compiler those parts of a program that are unreachable by design. See CppReference for more info.

Write the output routines early

The next function I tackled was display_order. That way, I could use it to see how the other routines were doing. This function, literally, has only one statement!

void display_order(
    std::string const& person,
    double const total_appetizers,
    double const total_drinks,
    double const total_meals,
    double const total)
{
    std::cout << std::format(
        " Order for {}:"
        "\n"
        "\n   Appetizers  ${:10.2f}"
        "\n   Drinks       {:10.2f}"
        "\n   Meals        {:10.2f}"
        "\n   -----------------------"
        "\n   Total       ${:10.2f}"
        "\n\n"
        , person
        , total_appetizers
        , total_drinks
        , total_meals
        , total
    );
}

My "top-down" program still compiles and runs. And that's the whole point. Stub functions allow you to run a program that is only half-finished.

Here is the output so far:

**********************************
       HOGWARTS RESTAURANT
**********************************

 Welcome to Hogwarts.

 How many people are included in your order? 1

 Person 1, what is your name? Steph Curry

 Hi, Steph Curry. What would you like to order?

**********************************
            Order Menu
**********************************
 1. Appetizer
 2. Drinks
 3. Meal
 4. I'm done!

 Choice? 4

 Order for Steph Curry:

   Appetizers  $      0.00
   Drinks             0.00
   Meals              0.00
   -----------------------
   Total       $      0.00

 Total for all orders: $ 0.00

Code / test / debug / forget

Top-down design tackles one stub at a time. You try to write a complete function that you can compile, test, and debug. Once you have it working, there is a chance you may never have to revisit it. You can just forget it, and move on to the next stub.

The goal is to write complete functions that are in their final form. The stubs are completely empty, and the finished functions are completely finished. There's no half-and-half.

This is a powerful programming technique for projects that have tens of thousands of lines of code, or more. You cannot hold such a large program in your head all at once. You have to be able to forget about the functions that are finished.

Here are two more for you. Function how_many is interesting, because it puts a lower limit on the values that can be entered, but does not have any upper limit. It takes advantage of the default for parameter max that is built into function get_integer.

int how_many(std::string const& item_name)
{
    int n = get_integer(
        std::format(" How many {} do you want? ", item_name), 0);
    std::cout << '\n';
    return n;
}
double appetizer_order()
{
    double const price_Dumbledonuts = 2.99;
    double const price_Potterfries = 5.99;
    double const price_Griffintoes = 4.99;

    std::cout << std::format(
        "\n**********************************"
        "\n          Appetizer Menu"
        "\n**********************************"
        "\n 1. Dumbledonuts    ${:5.2f}"
        "\n 2. Potterfries     ${:5.2f}"
        "\n 3. Griffintoes     ${:5.2f}"
        "\n"
        "\n"
        , price_Dumbledonuts, price_Potterfries, price_Griffintoes
    );
    enum : int { Dumbledonuts = 1, Potterfries, Griffintoes };
    int choice = get_integer(" Choice? ", 1, 3);
    std::cout << '\n';
    switch (choice)
    {
    case Dumbledonuts: 
        return price_Dumbledonuts * how_many("Dumbledonuts");
    case Potterfries: 
        return price_Potterfries * how_many("Potterfries");
    case Griffintoes: 
        return price_Griffintoes * how_many("Griffintoes");
    default: 
        std::unreachable();
    }
    std::unreachable();
}