I have this function that takes a string from main. The string contains all of the valid characters that a user can input from some menu options. Function will put character input into a variable and be compared to each character of the string. Compare input variable to the string characters until valid input is entered.
My question is, what is the best way to implement this loop? I don't like using while (true) with a return in the middle because it looks like an infinite loop with an exception in the middle, which makes it slightly harder to read, but I'm not sure how else I can do what I want it to do. What's the best practice for achieving my goal? Thanks.
char getValidKey(string validKeys)
{
char letter;
while (true) {
cout << "Operation ? ";
cin >> letter;
cin.ignore(numeric_limits<streamsize>::max(), '\n');
for (int i = 0; i < validKeys.length(); i++) {
if (letter == validKeys[i])
return letter;
}
cout << "Error. Invalid input.\n";
}
}
Also, I have a switch statement with multiple returns. Is it more common/preferred to assign calculations to a variable and have one return at the end or is this way generally okay?
string opStr;
switch (myOperation) {
case 1:
opStr = "RIGHT";
break;
case 2:
opStr = "LEFT";
break;
case 3:
opStr = "CENTER_ONLY";
break;
case 4:
opStr = "CENTER_MISSING";
break;
default:
opStr = "Error. Invalid input.";
break;
}
return opStr;
OR
switch (myOperation) {
case 1:
return "RIGHT";
break;
case 2:
return "LEFT";
break;
case 3:
return "CENTER_ONLY";
break;
case 4:
return "CENTER_MISSING";
break;
default:
return "Error. Invalid input.";
break;
}
For the first case, refactor your code in smaller self-contained functions, and it becomes clear to understand the logic of getValidKey even from a while(true):
char isKeyValid(char x, const string& validKeys)
{
return validKeys.find(x) != string::npos;
}
char readCharFromCin()
{
char letter;
cout << "Operation ? ";
cin >> letter;
cin.ignore(numeric_limits<streamsize>::max(), '\n');
return letter;
}
char getValidKey(const string& validKeys)
{
while (true)
{
const char key = readCharFromCin();
if(isKeyValid(key, validKeys)) return letter;
cout << "Error. Invalid input.\n";
}
}
For the second case, avoid break and simply return from your switch. Make the function containing the switch only do one thing.
string switchOperation(int myOperation)
{
switch (myOperation)
{
case 1: return "RIGHT";
case 2: return "LEFT";
case 3: return "CENTER_ONLY";
case 4: return "CENTER_MISSING";
}
return "Error. Invalid input.";
}
Also, try to maximize usage of const and pass string instances you're only reading by const& to avoid unnecessary copies.
Related
Basically I want to accept a particular character from a user and then using a switch case it passes the string related with that character case to another function .for eg.
case i:strcpy(str,"ice-cream");
other_function(str);
break;
If any default character got printed by the user then it should print default statement and again take character from user and check its case. I did this with goto, but is there any other option available to avoid or replace goto in this code.
p:
{
cout<<"Choose account type:-\n"<<"Enter\ns :-saving\nc :-current\nf :-fixed\nr :-recurring"<<endl;
char c;
cin>>c;
switch(c)
{
char t[20];
case 's':
strcpy(t,"saving");
a[i].setype(t);
break;
case 'c':
strcpy(t,"current");
a[i].setype(t);
break;
case 'f':
strcpy(t,"fixed");
a[i].setype(t);
break;
case 'r':
strcpy(t,"reccurring");
a[i].setype(t);
break;
default:
cout<<"Enter valid account type"<<endl;
goto p;
}
}
The whole switch should be hived off into a function, and its return value used to determine what happens to the loop next.
while (true) {
std::cout << "Choose account type:\n" << std::flush;
char c;
if (cin >> c)
{
const bool result = HandleUserInput(a[i], c);
if (result)
break;
else
std::cout << "Enter valid account type\n";
}
else
{
// Input error - you'll want to do something about that
}
}
And:
// Given a character representing an account type, sets
// the type of 'account' as appropriate. Returns false
// on failure.
bool HandleUserInput(Account& account, const char c)
{
switch (c)
{
case 's':
a[i].setype("saving");
return true;
case 'c':
a[i].setype("current");
return true;
case 'f':
a[i].setype("fixed");
return true;
case 'r':
a[i].setype("recurring");
return true;
default:
return false;
}
}
(Notice how I got rid of the strcpy, which doesn't appear to be necessary? Depends what setype [which is misspelled] does I suppose. Also, for a bonus point, consider using a map instead of the switch, if you don't care about a bit of a performance hit.)
Bathsheba's suggestion is a valid alternative, though I suggest that a return will look much clearer inside a switch than will a continue, since the latter has meaning within other kinds of control flow statements, whereas the former never does.
Also note that, if you decide not to use a function for some good reason, there's actually nothing particularly wrong with your goto, and don't let the cargo cultists tell you otherwise!
Yes there is. Since continue doesn't have a direct meaning in a switch block (cf. break), the presence of the former will apply to an appropriate outer loop control structure.
So something on the lines of
do {
// your code here, starting with "Choose account type".
...
default:
std::cout << "Enter valid account type" << std::endl;
continue; // back to the start of the do loop
} // close the switch
break; // fall out of the loop
} while (true);
will do it, and is fairly idiomatic C++.
Use a boolean flag:
bool isInputSuccessful = false;
while (!isInputSuccessful)
{
cout<<"Choose account type:-\n";
char c;
cin>>c;
isInputSuccessful = true;
switch(c)
{
char t[20];
case 's':strcpy(t,"saving");
a[i].setype(t);
break;
case 'c':strcpy(t,"current");
a[i].setype(t);
break;
case 'f':strcpy(t,"fixed");
a[i].setype(t);
break;
case 'r':strcpy(t,"reccurring");
a[i].setype(t);
break;
default:cout<<"Enter valid account type"<<endl;
isInputSuccessful = false;
}
}
Before inputting from the user, this code sets the success flag to true, and if unsuccessful, returns it to false.
Alternatively, it could just set it to true in each successful case.
I suggest dividing your code into a few functions. That will make it is easier to understand what each function is doing and how it is doing.
bool isValidAccountType(char c)
{
return ( c == 's' || c == 'c' || c == 'f' || c == 'r');
}
char getAccountType()
{
char c;
cout<<"Choose account type:-\n"<<"Enter\ns :-saving\nc :-current\nf :-fixed\nr :-recurring"<<endl;
while ( cin >> c )
{
if ( isValidAccountType(c) )
{
return c;
}
cout<<"Enter valid account type"<<endl;
}
// Wasn't able to get input.
// Exit out of the program.
exit(0);
}
void processAccount(char c)
{
char t[20];
switch(c)
{
case 's':strcpy(t,"saving");
a[i].setype(t);
break;
case 'c':strcpy(t,"current");
a[i].setype(t);
break;
case 'f':strcpy(t,"fixed");
a[i].setype(t);
break;
case 'r':strcpy(t,"reccurring");
a[i].setype(t);
break;
default:cout<<"Invalid account type"<<endl;
return;
}
// Rest of the function.
}
Use the following in main.
char c = getAccountType();
processAccount(c);
If you put this code into a function, you can use a return statement to exit the loop:
const char* enter() {
for (;;) {
std::cout << "Choose account type: ";
char ch;
std::cin >> ch;
switch(ch) {
case 's': return "saving";
case 'c': return "current";
case 'f': return "fixed";
case 'r': return "recurring";
}
std::cout << "Invalid input.\n";
}
}
Now you can call this function and use its result:
char t[20];
strcpy(t, enter());
a[i].set_type(t);
While all the other examples are very interesting, I usually stay away from true in the loop condition wherever it is possible.
In this case it would be correct to move the handling of the cases into a function by itself and use the returning result of the function to continue the actions.
First declare some predefined return results.
enum class Action_Result
{
Ok,
Error,
Invalid_account,
Quit
/*...*/
};
Next make the function return the predefined results. (Notice that instead of break in the case, return is used to break out the function and also return the action result.
Action_Result handle_account_type(char c /*...*/)
{
switch (c)
{
char t[20];
case 's':
strcpy(t, "saving");
a[i].setype(t);
return Action_Result::Ok;
case 'c':
strcpy(t, "current");
a[i].setype(t);
return Action_Result::Ok;
case 'f':
strcpy(t, "fixed");
a[i].setype(t);
return Action_Result::Ok;
case 'r':
strcpy(t, "reccurring");
a[i].setype(t);
return Action_Result::Ok;
default:
return Action_Result::Invalid_account;
}
}
Then in the main loop we can make a decision based on the result of the handling function. Notice how the loop condition is now easy to understand, why the loop will continue to execute, and when it will stop looping. Also all the input and output is in the main function, separate from the actions (adhering better to the Single Responsibility Principle) .
int main()
{
Action_Result account_handled_result = Action_Result::Error;
do
{
cout << "Choose account type:-\n"
<< "Enter\ns :-saving\nc :-current\nf :-fixed\nr :-recurring"
<< endl;
char c;
if (cin >> c)
{
if (c == 'q')
account_handled_result = Action_Result::Quit;
else
account_handled_result = handle_account_type(c);
}
else
{
account_handled_result = Action_Result::Error;
}
if (account_handled_result == Action_Result::Invalid_account)
cout << "Enter valid account type" << endl;
} while (account_handled_result != Action_Result::Quit);
}
I'm having a problem with a program I've built.
It should take input from the user and check whether it's 'P' or 'M'.
The problem is that I only want it to work if you enter 'P' or 'M', as it is now it accepts as 'M' anything you type as long as it starts with an 'M' (eg. if you type "morse" it will accept it as 'M').
I'm not a programmer and don't have much knowledge of c++, I just made it for fun. An example of how it is:
int main(){
std::cout << "Enter 'M' or 'P'\n";
char slction;
Inputrror:
std::cin >> slction;
switch (slction) {
case 'M':
goto Morse;
break;
case 'm':
goto Morse;
break;
case 'P':
goto Text;
break;
case 'p':
goto Text;
break;
default:
std::cout << "Please only enter 'M' or 'P'\n;
goto Inputrror;
break;
}
Morse:
std::cout << "Morse\n;"
return 1;
Text:
std::cout << "Text\n;"
return 1;
}
EDIT: I tried to read the input as a string like it was suggested and it now works properly. The correct version:
int main() {
std::cout << "Enter 'M' or 'P'\n";
std::string slction;
Inputrror:
std::cin >> slction;
if (slction == "M" || slction == 'm') {
goto Morse;
}
else if (slction == "P" || slction == 'p') {
goto Text;
}
else {
std::cout << "Please only enter 'P' or 'M'\n";
goto Inputrror;
}
Morse:
std::cout << "Morse\n";
return 1;
Text:
std::cout << "Text\n";
return 1;
}
One comment before I answer:
Instead of
case 'M':
goto Morse;
break;
case 'm':
goto Morse;
break;
you could use
case 'M':
case 'm':
goto Morse;
break;
break stops the block so as long as you don't use it you can nest one after another. You can even do stuff like:
case 'M':
cout << "CAPITALIZED";
case 'm':
goto Morse;
break;
Now, to your question: you are reading a char, meaning it will only take the first letter you input. Use a string instead if you want to be able to read words too:
string slction;
cin >> slction;
PD: remember to change the case 'M' and other options' quotes to double quotes (for strings)
PD2: you can't use switch with strings, so you will have to use if/else blocks
With what was said in the first answer, additionally you could use #include <cctype> toupper() function to remove extra cases. As well as validate your input with if statements.
example validation function:
char isValid(char &selection){
std::cin >> selection;
selection = toupper(selection); // ctype.h for toupper changes all to uppercase characters
//checks to see if more than 1 character is inputed
if (std::cin.get() != '\n'){
std::cin.ignore(256, '\n'); //ignores 256 chars until newline('\n')
std::cin.clear(); // clears the input
selection = '\0'; // sets selection to null
}
return selection;
}
DEMO
As the title suggests, I have a working program for when a user inputs A B or C. My professor has said that we have not gone over repetition yet so we just need to put in a line of code that returns something like "Please enter either A B or C" when the user enters any other character but I am having trouble figuring out how to do this. Any help will be very appreciated. I'll post a file of the code I have now.
https://docs.google.com/document/d/1EVxLPtOsBbdmCCt0LwUDYkqgySg8bSm3w_d_CAcGW6g/edit?usp=sharing
Here is a common stencil for processing menus:
bool invalid_selection = true;
while (invalid_selection)
{
// Output the menu with choices
// ...
char choice;
std::cin >> choice;
choice = std::toupper(choice);
switch (choice)
{
case 'A':
do_something;
break;
// ... other choices ...
default:
std::cout << "Invalid choice.";
}
if (choice == quit_character)
{
break; // exit out of the loop
}
}
There are many other alternatives. For example, one is a do-while loop.
If you don't know about switch, use your if-else-if ladder. The final else clause is equivalent to the default case.
EDIT: It'd be even better to use a std::string as a buffer to prevent receiving multiple errors from this if the user inputs more than one char.
The best way to handle this would probably be a simple do... while nested switch statement:
#include <string>
bool repeat = true;
do {
std::string buffer;
cout << "Which plan do you want to use?" << endl;
cin >> buffer;
// check if the user entered only one character
if (buffer.length() > 1) {
cout << "Invalid Input" << endl;
continue;
}
plan = buffer[0];
switch(plan) {
case 'A':
// do things
repeat = false;
break;
case 'B':
// do things
repeat = false;
break;
case 'C':
// do things
repeat = false;
break;
default:
cout << "Invalid input, please try again." << endl;
break;
} while (repeat);
This keeps asking the user for which plan they want to use, until you receive valid input.
Note: OP did not want a complete refactoring of his code. So this is the minimal-intervention solution.
Edit your if statements to:
if(plan=='A'){
//...
} else if(plan=='B'){
//...
} else if(plan=='C'){
//...
} else {
//handle the error here.
cout << "Wrong input" << endl;
}
I'm working on a final assignment for an Intro to C++ course. What I've coded so far works, but it's producing some interesting output that I'm looking for clarification on. Here's my code:
(Caveat: Yes, I know using void main() sucks, but we're using Visual Studio in class, and this is the instructors preference.)
#include <string>
#include <iostream>
using namespace std;
void conversion(int);
void main()
{
int decimal_number, answer;
cout << "Please enter a whole decimal number (e.g. 20): ";
cin >> decimal_number;
if (decimal_number == 0)
{
answer = 0;
cout << "The hexadecimal value of your number is: " << answer;
getchar();
getchar();
}
else if (decimal_number < 0)
{
cout << "INVALID ENTRY" ;
getchar();
getchar();
}
else if (decimal_number > 0)
{
conversion(decimal_number);
}
getchar();
getchar();
}
void conversion (int decimal_number)
{
int count = 0, remainder, reverse_order;
char hexadecimal_number[10] = { NULL };
while (decimal_number != 0)
{
remainder = decimal_number % 16;
switch (remainder)
{
case 0:
hexadecimal_number[count] = '0';
count++;
break;
case 1:
hexadecimal_number[count] = '1';
count++;
break;
case 2:
hexadecimal_number[count] = '2';
count++;
break;
case 3:
hexadecimal_number[count] = '3';
count++;
break;
case 4:
hexadecimal_number[count] = '4';
count++;
break;
case 5:
hexadecimal_number[count] = '5';
count++;
break;
case 6:
hexadecimal_number[count] = '6';
count++;
break;
case 7:
hexadecimal_number[count] = '7';
count++;
break;
case 8:
hexadecimal_number[count] = '8';
count++;
break;
case 9:
hexadecimal_number[count] = '9';
count++;
break;
case 10:
hexadecimal_number[count] = 'A';
count++;
break;
case 11:
hexadecimal_number[count] = 'B';
count++;
break;
case 12:
hexadecimal_number[count] = 'C';
count++;
break;
case 13:
hexadecimal_number[count] = 'D';
count++;
break;
case 14:
hexadecimal_number[count] = 'E';
count++;
break;
case 15:
hexadecimal_number[count] = 'F';
count++;
break;
default:
cout << decimal_number << "+++ " << hexadecimal_number;
cout << "INVALID ENTRY";
getchar();
getchar();
}
decimal_number = decimal_number / 16;
}
cout << "The hexadecimal value of your number is: ";
for (reverse_order = count -1; reverse_order >= 0; reverse_order--)
{
cout << hexadecimal_number[reverse_order];
}
getchar();
getchar();
}
So, like I said: my code works. I can take any number input as a decimal, and convert it to its hexadecimal equivalent. However, I've found that I've had to include an IF statement within the main function of the code, because if the user inputs anything other than a decimal number into the decimal_number variable, the program will store a string of decimal numbers, into decimal_number, and I have no idea where those numbers come from. They don't appear to be the ASCII equivalents of anything.
... If none of this makes any sense, I'm sorry. just input cout << decimal_number after the line cin >> decimal_number, then run the code and see what weird number comes out. I hope that makes things clearer.
Anyway, my instructors stumped, and I'm stumped. I've got the above workaround in place that the instructor will accept, but for my own sanity, I just want to figure out what's going on. Any help or pointers is appreciated. Cheers!
You can test whether the result of cin >> decimal_number succeeded, like
if(!(cin>>decimal_number))
throw std::runtime_error("Oops, not a decimal number!");
This is a bit too extreme, you can also validate the input:
while(!(cin>>decimal_number))
{
std::cout << "Not decimal, input again ";
cin.clear();
cin.ignore(numeric_limits<streamsize>::max(), '\n');
}
If you're not performing this kind of validation, then you leave the stream in an invalid state whenever reading a non-decimal, and the variable you think you're reading into will end up un-initialized.
However, I've found that I've had to include an IF statement within the main function of the code, because if the user inputs anything other than a decimal number into the decimal_number variable, the program will store a string of decimal numbers, into decimal_number, and I have no idea where those numbers come from.
Well, you did not initialise decimal_number to anything, and you do not have any error checking around the cin >> decimal_number call. So I'm not sure what else you expected but an unspecified value for decimal_number!
Your instructor should know this. It's worrying that, on top of teaching you to write code that is ill-formed per the International Standard (void main!!), they failed to discover this problem or note that you have no error checking.
What's the point of the assignment?
The conversion can be simplified to:
cout << hex << decimal_value << endl;
Or if you need it in a string:
std::string convert_decimal_to_hex_string(int decimal_value)
{
std::ostringstream output;
output << hex << value;
return output.str();
}
I believe the class should show you how to use std::string and existing language features (such as the hex manipulator). Using char for a string is dangerous.
Also, since you don't know the size or limit of the decimal values, you will need to dynamically allocate (i.e. during run-time) the array holding the characters. Think about allocating 2 characters and entering the value 1024; buffer overflow.
Change your program to use std::string. Refrain from character (C-Style) arrays.
I am facing problem in the below code. If the user enter more than one charater then my loop gets executed number of times equal to the length of the string entered by the user. My code is written in GNU c/c++ compiler.
Thanks in advance.
int continue_option()
{
char c;
loop:
fflush(stdin);
cin.ignore();
cout<<"\n\n\t\t\t\tPress (Y/y) - Continue / Press (N/n) - Exit :";
cin>>c;
if(c=='y'||c=='Y')
{
system("clear");
}
else if(c=='n'|| c=='N')
{
exit(0);
}
else
{
printf("\n\t\t\t\tInvalid Option.Try Again.....");
goto loop;
}
fflush(stdin);
}
First thing, don't use jumps. They are old style, and they make Dijkstra spin in his grave, on top of all the other bad consequences. I don't mean "vintage", I really mean old in the bad sense.
As of your question, I'd rather put the result in a std::string and only consider the first character in there:
std::string input;
std::cin >> input;
switch (input[0]) {
case 'y':
case 'Y':
//your code
break;
case 'n':
case 'N':
exit(0);
default:
std::cout << "Invalid text" << std::endl;
}
I would also refrain from using exit(), I'd rather rely on a function's return value to finally cause a return 0; in the main(), or some equivalent technique.
You can't stop the user from typing more than one character.
What you can do is ignore the rest of the line. You have already use cin.ignore() which ignores one character. You can use cin.ignore(large number) to ignore up to the large number or the end-of-line, whichever appears first.
Unlike flushing output files, fflush(stdin) doesn't really do anything.
Try using cin.get() or getch() to read just one character at a time. Also, I guess you'd be better off replacing the whole thing with a simple loop like:
char ch = '\0';
do
{
ch = getch();
}while((tolower(ch) != 'y') || (tolower(ch) != 'n'))
if(tolower(ch) == 'y')
{
//additional handling
}
else
{
exit(0);
}
Not exactly the same behavior, but should put you on track:
#include <iostream>
#include <iomanip>
bool is_valid_answer(char c)
{
switch(c)
{
case 'y':
case 'Y':
case 'n':
case 'N':
return true;
default:
return false;
}
}
bool continue_option()
{
std::cout << "Press (Y/y) to continue, (N/n) to exit: " << std::flush;
char c = '\0';
while (std::cin.get(c) && !is_valid_answer(c));
return ((c == 'y') || (c == 'Y'));
}
int main()
{
std::cout << "Continue option: " << continue_option() << std::endl;
}