My switch statement is not exiting the loop cleanly - c++

I am currently testing out my driver program for my homework. What happens is that I have a menu printed with different options, and the program uses a switch statement based on the user's input to determine what to do. Everything works fine except for the "exit program" case, where it's supposed to leave the program and end it. Instead, it will print out the message "Quitting program" (like it's supposed to) and then follow up with doing one of the first 4 cases. It doesn't actually leave the switch statement. I don't know what could be wrong because I've used this method before and have not encountered this issue.
#include <iostream>
#include <fstream>
#include <cstring>
#include <string>
#include "ListRecords.h"
#include "BookRecord.h"
using namespace std;
//Prints the menu for the user.
void printMenu(){
cout << "\n\n1) Insert a book record into the list\n";
cout << "2) Print information of a book with the given ISBN number\n";
cout << "3) Print the list of books, sorted by ISBN (lowest to highest)\n";
cout << "4) Print the list of books, sorted by title\n";
cout << "5) Quit the program\n";
cout << "Option: ";
}
//Menu block of the code. Takes in command choice and performs appropriate functions
void action(ListRecords books, int x){
cin.sync();
int option;
switch (x){
case 0: {
printMenu();
cin >> option;
action(books, option);
}
case 1: {
string title, author, pub;
long isbn;
cout << "\n\t**Inserting a new book into the record**";
cout << "\nTitle: ";
getline(cin, title);
cout << "Author: ";
getline(cin, author);
cout << "Publisher: ";
getline(cin, pub);
cout << "ISBN: ";
cin >> isbn;
BookRecord sample = BookRecord(title, author, pub, isbn);
books.insertBookInfo(sample);
cout << "\n\tNew book has been entered into the record\n\n";
printMenu();
cin >> option;
action(books, option);
}
case 2: {
long printISBN;
cout << "Printing book with ISBN number: ";
cin >> printISBN;
books.printBookInfo(printISBN);
cout << "\n\n";
printMenu();
cin >> option;
action(books, option);
}
case 3: {
cout << "\n\t**Printing all books in ISBN increasing order**\n";
//books.quickSortNum(0, books.seeFillSize());
books.rearrangeList(0);
books.printAll();
printMenu();
cin >> option;
action(books, option);
}
case 4: {
cout << "\n\t**Printing all books in alphabetical order**\n";
//books.quickSortNum(0, books.seeFillSize());
books.rearrangeList(1);
books.printAll();
printMenu();
cin >> option;
action(books, option);
}
case 5: {
cout << "\n\t**Quitting program. . .**\n";
return;
}
//For the purposes of debugging, I placed option 6 to print all books
case 6: {
books.printAll();
printMenu();
cin >> option;
action(books, option);
}
default: {
cout << "\n\t**ERROR: Invalid input. Try again**\nEnter option: ";
cin >> option;
action(books, option);
}
}
}
int main(void){
string fileName;
int option;
//Prompt for file name
cout << "Enter the name of the file: ";
cin >> fileName;
cout << "\nLoading the file and its contents. . .";
//Create a List Records object and clear stream
ListRecords books = ListRecords(fileName);
cin.sync();
cout << "\n\n";
//Start menu process. Beginning option is 0
action(books, 0);
//Once user quits, this will happen
cout << "\n Thanks for trying out this program!\n";
system("pause");
return 0;
}

The root cause of your problem is that your code uses recursion instead of iteration.
main makes a call to action, which goes into a switch, which then calls action again, which goes into the switch and calls action, until the Quit option is selected.
At this point the recursive call chain starts to unwind. Unfortunately, since your code lacks break statements, the switch is not exited immediately. Instead, the code falls through to the next case label, making you think that the return did not do its job at terminating the action. It did, but only the last action on the call stack is terminated. The remaining ones are still in progress, so they would continue as soon as the higher-level action finishes.
You can add break statements to your switch statement to mask the problem. However, the root cause would not go away: your program would remain poorly organized, and hard to read.
Consider rewriting the code using a while loop in the action function. Keep the switch, add breaks, and remove recursive calls to action from inside the switch. Instead, let the loop continue, so that the switch is re-entered and processed again, until the Quit option is selected.

First thing you do is call action(books, 0); to get to the menu.
switch (x){
case 0: {
printMenu();
cin >> option;
action(books, option);
}
There you call action(books, option); with the user supplied number.
You continue doing this for every option untill the user enters a 5 to end the program.
Because you don't have any break statements the code will go back to the 'calling case' and continue executing into the next case.
Be sure to end a case with a break to not continue executing the next case block (fall through).
Here is another questionn about why a break is needed.

In main() you call action(book, 0) so it enters action function. Then you prompt for choice and enter action() gain with that option. Then you enter 5 and quit that action, the return address of the nested action() call returns just after that call, at case 1: block and continues to execute it.
So what you need to do is put break statement at the end of each block.
When you don't use break after once case block, the code continues executing next case block.
So as the guy above said, you should consider placing break at the end of each block.
Also, you might want to consider not using recursion, but maybe iteration, and put prompt block outside the switch/case.

Related

How to add a string into a vector in a switch case menu in C++

The task I have is to make a vector of strings, append strings to it, and delete strings from it. I am having trouble with append strings.I also have it set up so that the switch case gives the option to append the queue in case 1.
//gaming Queue
#include <iostream>
#include <vector>
#include <algorithm>
#include <ctime>
#include <cstdlib>
using namespace std;
int main()
{
int choice;
string input;
bool menu = true;
vector<string> favGames;
while (menu){
cout <<"Welcome to the favorite game queue please add your favorite games:\n";
cout <<"1-Add a favorite game.\n";
cout <<"2-List of your favorite games.\n";
cout <<"3-Remove a game.\n";
cin >> choice;
switch (choice)
{
case 1:
cout << "Please add a favorite game to the queue: \n";
string input;
cin >> input;
favGames.push_back(input);// Here is my problem it just jumps to case 2 and shows an error
break;
case 2:
cout << "Here is a list of your favorite games.\n";
break;
default:
cout << "You made an illegal choice.\n";
}
}
return 0;
}
switch statements are a bit weird because you can't declare variables inside the case clauses unless you create a scope for them using a {} block.
switch (choice)
{
case 1:
{ // start a scope
cout << "Please add a favorite game to the queue: \n";
string input;
cin >> input;
favGames.push_back(input);// Here is my problem it just jumps to case 2 and shows an error
break;
} // end the scope
However in your case you already defined a std::string outside the switch, did you intend to use that? Then you can just remove the one inside the case clause.
When compiling your code on https://www.onlinegdb.com/online_c++_compiler, it shows me those error:
main.cpp:34:18: error: jump to case label [-fpermissive]
case 2:
^
main.cpp:30:24: note: crosses initialization of 'std::string input'
string input;
as you can see the compiler tells you that you're skipping the initialization of string input. At the same time you are declaring input a second time.
By removing input inside the switch case, the program compiles and works as intended.
EDIT:
You can't enter more than one word using cin because cin extraction always considers spaces (whitespaces, tabs, new-line...) as terminating the value being extracted.
So you have to use getline. Same for when getting choice.
here is the full code:
#include "stdafx.h"
#include <vector>
#include <string>
#include <iostream>
#include <sstream>
using namespace std;
int main()
{
int choice;
string input;
bool menu = true;
vector<string> favGames;
while (menu) {
cout << "Welcome to the favorite game queue please add your favorite games:\n";
cout << "1-Add a favorite game.\n";
cout << "2-List of your favorite games.\n";
cout << "3-Remove a game.\n";
string choiceStr;
getline(cin, choiceStr);
stringstream(choiceStr) >> choice;
switch (choice)
{
case 1:
cout << "Please add a favorite game to the queue: \n";
getline(cin, input);
favGames.push_back(input);
break;
case 2:
cout << "Here is a list of your favorite games.\n";
break;
case 3:
cout << "No longer like a game which game should we remove?\n";
break;
default:
cout << "You made an illegal choice.\n";
}
}
return 0;
}
The reason why it enters infinite loop:
The cin >> input can only work when you enter only one word. If you enter several words, the cin >> input will catch the first one then the cin >> choice will catch the next one. If the input catch by cin >> choice is not an int, the cin will fail which makes you an infinite loop in this case.
It is explained here http://www.cplusplus.com/doc/tutorial/basic_io/.

Stuck in loop within a function (C++)

I have written a program with several menus within it. I am now debugging the program and I wanted to include some input validation in the menu choices. However, for some reason, when it detects a wrong input it goes back to the beginning of the function with a goto statement (I know, bad practice :\) and It asks the user for a new input, but even if the input is right, it goes back to the case for non allowed inputs (default) no matter what. Does anyone have any idea of what's going on?
NOTE:
select_variable_check(vector<int> , int) is a function that checks if the value entered has been entered before if that is of any relevance, although I don't think it has anything to do with it.
void select(vector<int>&select_control) {
char select;
choices:
cin >> select;
int selectint = select;
bool check = select_variable_check(select_control, selectint);
switch (select) {
case ('1','2','3','4','5','6','7','8','9','10'):
if (check == false) {
string city = city_selection(selectint);
double xcoor = xcoor_selection(selectint);
double ycoor = ycoor_selection(selectint);
cout << "\n" << city << "\n";
select_control.push_back(selectint);
cout << "\n Enter next city: ";
cin >> select;
selectint = select;
}
else {
cout << "You have already selected that city, please select another one ";
cin >> select;
}
break;
case '99': {
cout << "TERMINATING" << endl;
Sleep(3000);
exit(0);
break;
}
case '100': {
cout << "input complete" << endl;
break;
}
default: {
cout << "not a valid value, please try again" << endl;
goto choices;
break;
}
}
The value of ('1','2','3','4','5','6','7','8','9','10') is '10', so that's the only value that will trigger that first case statement. The right way to write this is:
case '1':
case '2':
case '3':
...
Even with this change, though, '10' is a peculiar kind of character, and almost certainly not the right thing here.
Your code boils down to
start:
get_input
process_input
if good do something
else go to start
end:
Now when you enter bad input it goes back to start. Your input operation will fail again as the input stream is still in an error state so you do not get new input and since you have bad input you go back to start. To stop this loop you need to clear out the error flags on the stream and remove any input still in the buffer. That will make you default case look like
default: {
cout << "not a valid value, please try again" << endl;
cin.clear(); // removes error flags
cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // flushes input buffer
goto choices;
break;
}
You will need to #include <limits> to use cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n')

loop not working?

After the call for Back to Main Menu, it returns to the mainMenu but when option or command is typed, the option is not accepted or the loop not working. Wonder where is the mistake? Is it extra call should be added or?
#include <iostream>
using namespace std;
char mainMenu(void);
int factorial(int n);
unsigned long long combination(long nK, long nR);
int main(){
char option;
int shape,function,i,j,k,t,n;
long nK, nR;
unsigned long long COM;
while((option=mainMenu())!='0')
{
switch(option)
{
case '1'://Program 1:
cout<< "*Drawing a shape\n"
<< "(1-Rectangle, 2-Triangle, 3-Inverted Triangle, 4-Letter 'H', 0-Back to Main Menu)\n";
do
{
cout<< "Choose shape >> ";
cin>> shape;
cout<< endl;
switch(shape)
{
case 1: break;
case 2: break;
case 3: break;
case 4: break;
case 0:
//Back to Main Menu
cout<< "Back to main menu\n"
<< endl;
return mainMenu(); //After here, it does back to Main Menu but command or option is not working
}
}while(shape!=0);
case '2': //Program 2
cout<< "*Choose function of calculator\n"
<< "(1-Factorial, 2-Combination, 0-Back to main menu)\n";
do
{
cout<< "Choose function >> ";
cin>> function;
cout<< endl;
switch(function)
{
case 1: break;
case 2: break;
case 0:
cout<< "Back to main menu\n"
<< endl;
return mainMenu();
}
}while(function!=0);
case '0':
cout<< "Program is terminating\n"
<< endl;
return 0;
default:
cout<< "Wrong input. Please choose one of the above options.\n"
<< endl;
return mainMenu();
}
}
}
char mainMenu(void){
char option;
cout<< "##############################\n"
<< "Main Menu\n"
<< "Enter your command!\n"
<< "##############################\n"
<< endl
<< "1. Program1\n"
<< "2. Program2\n"
<< "0. Exit\n"
<< endl
<< "Command >> ";
cin>> option;
cout<< endl;
return option;
}
I'm not sure what your question is, but your code is missing 2 important things. First, you need break statements at the end of each case block, otherwise the program flow will continue on to the next case statement.
Second, the inner menu doesn't ever escape the inner while(1) loop. This is a possible case for a goto use, although in practice it would better to refactor the code to split the top menu and inner menu into two functions, and use a return in the inner menu to return to the outer menu.
I'm not sure what your question is, but your code is missing 2 important things. First, you need break statements at the end of each case block, otherwise the program flow will continue on to the next case statement.
Second, the inner menu doesn't ever escape the inner while(1) loop. This is a possible case for a goto use, although in practice it would better to refactor the code to split the top menu and inner menu into two functions, and use a return in the inner menu to return to the outer menu.
As said, you're code is missing various things. It would be awesome if you distribute the entire code, and additionally the exact error message with line.
void value not ignored as it ought to be?...
...Is not that much of an explanation...
Also, are you sure you included iostream?
#include iostream
That said, you did not declare any of the variables used in the program.
You also missed a space in line 2 of your mainMenu() function.
Also, please tell us what you expected to happen.

else if looping quandry

I am in the second phase of a project where I need to extend my program into a menu driven application to query the database I have on a .txt file. So, my trouble is that I cannot get my loop to be perpetual. It always terminates when it initializes from one option to the next. Here is the snippet of my code that is my int main:
int main ()
{
char Q,q;
char S,s;
char task;
string pathname;
string z;
int count=0;
cout << "Welcome to Jason Rodriguez's Library Database." << endl;
cout << "Please enter the name of the backup file: ";
cin >> pathname;
ifstream inFile(pathname.c_str());
while(!inFile.eof())
{
getline(inFile,z);
count++;
}
while (task != 'Q' || task != 'q') {
cout << count << " records loaded successfully." << endl;
cout << "Enter Q to (Q)uit, Search (A)uthor, Search (T)itle, (S)how All: ";
cin >> task;
if ((task == 'Q')||(task =='q'))
{
cout << "Program will now terminate";
break;
}
else if ((task == 'S')||(task =='s'))
{
showAll (loadData (pathname));
cout << endl;
cout << "Enter Q to (Q)uit, Search (A)uthor, Search (T)itle, (S)how All: ";
cin >> task;
}
}
}
I need to add two more options into the loop on top of these two but I figured I should get the first two working correctly first. The other two should be plug & chug after that. Basically what I was trying to do is say if the user enters Q or q, terminate the program. Else, if user hits S or s, activate showall function and after ward, go back to the original query. It isn't working though. Assistance is welcome and appreciated.
Menus almost always require loops - especially ones that require the user to enter the correct choice input. The most applicable one in a case like this is the while loop - but essentially, any other loop variant can be used.
UPDATE:
int main ()
{
char task;//this is the only char needed. Your other chars were redundant
string pathname;
string temp;//I changed z to temp to better reflect its purpose
int count=0;
cout << "Welcome to Jason Rodriguez's Library Database." << endl;
cout << "Please enter the name of the backup file: ";
cin >> pathname;
ifstream inFile(pathname.c_str());//this is potentially a problem in that you aren't verifying that the pathname is a valid one
//you did not check to see that your file was open, otherwise there is no way to tell that you successfully opened the file
if (inFile.is_open()) {
//while(!inFile.eof()) is a character by character read and comparison
//made your life easier by shortening it down to this - which ensures
//that a line is read. (Much faster and more readable)
while(getline(inFile,temp))
{
count++;
}
inFile.close();//always close a file after you've used it
//At this point the entire file has been read. So, this is where this message SHOULD be
cout << count << " records loaded successfully." << endl;
}
else {
//if there was an error opening the file (i.e. wrong path, or it simply does not exist), this will be displayed
cout << "There was a problem opening your file" << endl;
exit(0);//and the program will terminate
}
while (task != 'Q' || task != 'q') {
cout << "Enter Q to (Q)uit, Search (A)uthor, Search (T)itle, (S)how All: ";
cin >> task;
if ((task == 'Q')||(task =='q'))
{
cout << "Program will now terminate";
break;
}
else if ((task == 'S')||(task =='s'))
{
string author;
//showAll (loadData (pathname));
cout << endl;
cout << "Search an Author" << endl;
cin >> author;//get the author name to search from the user
//write code to search an author here
}
}
}
There are a number of issues with the code that you posted which I will forgo for the sake of brevity. Hence, note the following:
Your code was printing the same message per option (except for quit). Of course it would appear that it didn't work. Each option is a different task. Print what each task does (similar to what I did).
You wish to search the file for an author, but you have not stored it. Look into a way of storing it that appeases your instructor.
It would be ideal for you to use switch in this case, considering the increasing complexity of your code.
Try breaking down each task into functions, and call them to make your main function readable. In fact, it is a good programming practice for your main function to be as small as possible.
And, as juanchopanza quite rightly pointed out: you have some fundamental issues with C++. Try doing some more exercises and do more examples from a good C++ book.

cin and switch odd behaviour

So I'm having some problems when using cin and switch statements.
First, the program prompts for an int:
cout << "How many registers would you like to use?: ";
cin >> kassor;
Later on, the user is asked to make a choice:
cout << endl << "Press \"S\" to run the queue simulator and/or make a timestep." << endl;
cout << "Press \"Q\" to quit." << endl << endl;
cout << "Choice: ";
cin >> choice;
switch (choice)
{
case 's':
case 'S':
{
cout << "test";
nya = newCustomers();
break;
}
case 'q':
case 'Q':
{
return 0;
}
}
Here, the 'q' option works fine, but the 's' option does not. It 'hangs', as if still waiting for input. I have tried various cin.ignore() and such, but to no avail.
What puzzles me is that
switch (choice)
{
case 's':
case 'S':
{
cout << "test";
nya = newCustomers();
break;
Gives nothing, but the following:
switch (choice)
{
case 's':
case 'S':
{
cout << "test";
cin.ignore(1024, '\n');
nya = newCustomers();
break;
outputs 'test'.
My main question here is: Is the problem cin or something in the case: s ?
Thank's in advance :
It looks like the function newCustomers is getting hung up on a stray character or two after the input to choice. That would explain why the ignore call "fixes" the problem, and it's the solution suggested in the comment by #TheBuzzSaw.
To see this more clearly, change
cout << "test";
to
cout << "test\n";
or to
cout << "test" << std::endl;
On some systems the console is line-buffered, so you won't see any output until the program writes a newline. Inserting endl flushes the output buffer, so you should see the message even if subsequent code hangs. Or change it to:
cerr << "test\n";
cerr is more aggressive about flushing buffers, precisely because it's used for error output that you don't want to miss.
Short Answer:
I believe cin is failing somewhere or it is the cause of unexpected behavior
I cannot pinpoint it without more code.
Edit: I cannot post a comment on your post, so I figured I would post here. The "hanging" could be an infinite loop. If you are doing something like what I am doing and looping to get input and fail to clear the error bits cin will constantly fail. If you are not cout'ing something each time you ask for input it could just be quietly sitting there looping through that input-gathering function. Put breakpoints in all of your loops and step through with the debugger to verify none of your loops are infinite looping.
Try adding a std::cin.clear(); before the .ignore() in your second test case. See if that stops your hanging.
Explanation:
cin can fail. This can cause weird behavior. A common way for it to fail is if you are reading into an integer and you get character input. Once cin fails it sets a fail bit and from then on, cin does not behave like you would expect.
I would recommend not using a bare cin >> choice, because it can fail and you wont know it. I would abstract this out to a method that gets the proper input you want.
I personally keep a tiny utility library(.h) and .cpp around to include in projects where I am using common functionality I have already coded.
I have a readIntBetween() function which accepts 2 integers and reads from standard input an integer between those two numbers. If the user does not provide the right input (integer over or under bounds, or input containing a character) I ask them to re-enter the input.
In this function I make sure to clear the fail bits when I have detected a failure, and I ignore something like 200 characters to "flush" it out.
Here is the code to my readIntBetween function. I hope it helps you diagnose your error and fix it:
int readIntBetween(int lower, int upper, std::string prompt)
{
bool goodVal = false;
int value = -1;
do
{
std::cout << prompt ;
std::cin >> value;
if ( value >= lower && value <= upper)//check to make sure the value is in between upper and lower
goodVal = true;
else
{
if(std::cin.fail())
{
std::cout << "\tError - invalid format for integer number" << std::endl;
//clear all status bit including fail bit
std::cin.clear();
//flush input buffer to discard invalid input
std::cin.ignore(1000, '\n');
}
else
std::cout << "Value is not valid, must be between " << lower<<" and "<<upper<<"."<<std::endl;
}
}while(!goodVal);
return value;
}
If your cin is being used inside a loop, I recommend a different approach.
char buffer[64];
cin.getline(buffer, 64);
char choice = buffer[0];
switch (choice)
{
...
}
It is possible your cin is not being reset, and any extraneous characters are being fed into the subsequent requests for input. Grab more input and only process its first character.