Recipe to nutrition converter - c++

I have the whole thing ready to go, but I'm down to one problem, my professor wants us to ask the user to put in how many servings there will be, my problem is here:
string recipeName, userInputStr, servingNumber;
int userInputInt;
double totalCals, totalCarbs;
// initialize accumlator variables
totalCals = 0.;
totalCarbs = 0;
// asking for name of recipe and number of servings
cout << "What is the name of your recipe? ";
getline(cin, recipeName);
cout << "How many servings would you like? ";
getline(cin, servingNumber);
istringstream(servingNumber) >> totalCals, totalCarbs;
if (servingNumber<1 || servingNumber>10)
{
cout << "You have entered the wrong answer. " << endl;
return 0;
}
more specifically, it's here:
if (servingNumber<1 || servingNumber>10)
I keep getting an error on the < and > saying "no operator matches these operands"
I was told I'm supposed to convert the string servingNumber to int then compare it, but isn't it converted here?:
istringstream(servingNumber) >> totalCals, totalCarbs;
and then compared on the if statement?
I'm not sure what I'm doing wrong..

You declared those variables as strings, but when you compare them to integer, no overloads exists that take those arguments, and there is no acceptable conversion. I think you meant for them to be integers:
string recipeNamem userInputStr;
int servingNumber; /*
^^^
You also don't need the istringstream either.
Also, instead of getline(cin, servingNumber), do cin >> servingNumber.

You are defining servingNumber as a string in your code. You do not compare a string with numbers, the operator is not defined for the string class.

You're getting this error because you're trying to compare a string with a number in your if statement. Beyond that there are some other things with this code that might be worth looking at.
The first is the way you define variables. In C the idiom was to define all the variables at the top of your function as you're doing here. However in C++ we prefer to define variables where they are used. The goal is to reduce the amount of thinking you need to do on a chunk of code:
string recipeName;
cout << "What is the name of your recipe?";
getline(cin, recipeName);
string servingNumber;
double totalCals;
double totalCarbs;
getline(cin, servingNumber);
istringstream(servingNumber) >> totalCals, totalCarbs;
if( servingNumber < 1 || servingNumber > 10)
{
cout << "You have entered the wrong answer. " << endl;
}
The next issue is that istringstream(servingNumber) >> totalCals, totalCarbs; doesn't do what you think it does. I don't know how you're planning to use totalCals and totalCarbs but right now this line only sets totalCals and does nothing to totalCarbs.
Finally your major issue is that you're trying to compare servingNumber (a string) to a raw integer. We need to convert servingNumber to an integer. Some people have recommended atoi which will do the job but is often considered unidiomatic C++. Since you're already using stringstream we can use that to convert our number like so:
string recipeName;
cout << "What is the name of your recipe?";
getline(cin, recipeName);
string servingNumberInput;
double totalCals;
double totalCarbs;
getline(cin, servingNumberInput);
istringstream(servingNumberInput) >> totalCals, totalCarbs;
int servingNumber;
istringstream(servingNumberInput) >> servingNumber;
if( servingNumber < 1 || servingNumber > 10)
{
cout << "You have entered the wrong answer. " << endl;
}
Alternatively if you're not depending on getline you can just stream the number directly in from cin like so:
string recipeName;
cout << "What is the name of your recipe?";
getline(cin, recipeName);
int servingNumber;
cin >> servingNumber;
if( servingNumber < 1 || servingNumber > 10)
{
cout << "You have entered the wrong answer. " << endl;
}
I got rid of the totalCarbs and totalCals stuff because we don't have any idea what they're doing at the moment.

Related

C++ Check if integer is of type int [duplicate]

I'm in my second OOP class, and my first class was taught in C#, so I'm new to C++ and currently I am practicing input validation using cin. So here's my question:
Is this loop I constructed a pretty good way of validating input? Or is there a more common/accepted way of doing it?
Thanks!
Code:
int taxableIncome;
int error;
// input validation loop
do
{
error = 0;
cout << "Please enter in your taxable income: ";
cin >> taxableIncome;
if (cin.fail())
{
cout << "Please enter a valid integer" << endl;
error = 1;
cin.clear();
cin.ignore(80, '\n');
}
}while(error == 1);
I'm not a huge fan of turning on exceptions for iostreams. I/O errors aren't exceptional enough, in that errors are often very likely. I prefer only to use exceptions for less frequent error conditions.
The code isn't bad, but skipping 80 characters is a bit arbitrary, and the error variable isn't necessary if you fiddle with the loop (and should be bool if you keep it). You can put the read from cin directly into an if, which is perhaps more of a Perl idiom.
Here's my take:
int taxableIncome;
for (;;) {
cout << "Please enter in your taxable income: ";
if (cin >> taxableIncome) {
break;
} else {
cout << "Please enter a valid integer" << endl;
cin.clear();
cin.ignore(numeric_limits<streamsize>::max(), '\n');
}
}
Apart from only skipping 80 characters, these are only minor quibbles, and are more a matter of preferred style.
int taxableIncome;
string strInput = "";
cout << "Please enter in your taxable income:\n";
while (true)
{
getline(cin, strInput);
// This code converts from string to number safely.
stringstream myStream(strInput);
if ( (myStream >> taxableIncome) )
break;
cout << "Invalid input, please try again" << endl;
}
So you see I use string for input and then convert that to an integer. This way, someone could type enter, 'mickey mouse' or whatever and it will still respond.
Also #include <string> and <sstream>
One minor quibble is that the error helper variable is completely redundant and is not needed:
do
{
cin.clear();
cout << "Please enter in your taxable income: ";
cin >> taxableIncome;
if (cin.fail())
{
cout << "Please enter a valid integer" << endl;
cin.ignore(80, '\n');
}
}while(cin.fail());
Might you not consider try/catch, just to get you used to the concept of exception handling?
If not, why not use a boolean, instead of 0 and 1? Get into the habit of using variables of the correct type (and of creating types where needed)
Cin.fail() is also discussed at http://www.cplusplus.com/forum/beginner/2957/
In fact, in many places ...
http://www.google.com.sg/#hl=en&source=hp&q=c%2B%2B+tutorial&btnG=Google+Search&meta=&aq=f&oq=c%2B%2B+tutorial
you might study some of those and try to follow the explanations of why things should be done a certain way.
But, sooner or later, you ought to understand exceptions...

What do you do in C++ if you ask for an integer and the user inputs "b"?

I'm new to statically typed C++. In JavaScript, I could just check the data type first, but that seems to be very complicated, and the answers all seem to imply that you aren't "getting" the language.
here's the code I was testing out rand() with, where I came upon the issue of converting strings to integers:
int main(){
std::string input;
cout <<endl<< "What to do?"<<endl;
cin >> input;
if (input == "rand")
{
cout << "what is the max?" << endl;
cin >> input;
int number;
if (stoi(input) > 1) {
number = stoi(input);
}
else {
number = 10;
cout << "using 10"<<endl;
}
cout << rand() % stoi(input);
return main();
}
}
so in Javascript, I would just check the type of input or result, but what do people do in C++?
Not allowed to say thank you in the comments so I'm saying thank you here!
Well, let's try out what happens: https://godbolt.org/z/1zahbW
As you can see, std::stoi throws an exception if you pass it invalid input or its input is out of range.
You should, however, be aware that std::cin >> some_string; is somewhat non-obvious in that it reads in the first "word", not a line or anything like that, and that std::stoi does the same thing (again).
One way to perform the check, could be like this:
#include <string>
#include <iostream>
int main(){
std::cout << "Please give me a number: " << std::flush;
std::string input;
std::getline(std::cin, input);
try {
auto value = std::stoi(input);
std::cout << "Thanks for the " << value << " (but the string was \"" << input << "\")\n";
} catch(std::invalid_argument const&) {
std::cout << "The provided value is not an integer\n";
} catch(std::out_of_range const&) {
std::cout << "The provided value is out of range\n";
}
}
https://godbolt.org/z/rKrv8G
Note that this will parse " 42 xyz" as 42. If that is a problem for your use case, you may wish to use std::strtoi directly, or to check if your input is valid before parsing (e.g., using a regex)
Regarding to the documentation of std::stoi it throws an std::invalid_argument.
What you could do is to place your std::stoi call inside a try and then catch the std::invalid_argument, but personally i wouldn't do that.
Instead, it is (most likely) a lot better to check if the first character of your input is an int, because if it is one, it can simply be parsed by std::stoi.
You can do that by e.g. doing the following:
int max = 0;
std::string input;
std::cin >> input;
if(std::isdigit(input[0]))
max = std::stoi(input);
EDIT: Please note that this would not respect the case of a too big number, to handle that case you would need an additional check.

Transversing an Array of structs error in C++

Current code:
const int MAX_CODENAME = 25;
const int MAX_SPOTS = 5;
struct Team {
string TeamName[MAX_CODENAME];
short int totalLeagueGames;
short int leagueWins;
short int leagueLoses;
};
//GLOBAL VARIABLES:
Team league[MAX_SPOTS];
void addTeams(){
int i = 0; //first loop
int j; //second loop
while(i < MAX_SPOTS){
cout << "****** ADD TEAMS ******" << endl;
cout << "Enter the teams name " << endl;
scanf("%s", league[i].TeamName) ;
}
void searchTeam(){
string decider[MAX_CODENAME];
cout << "Please enter the team name you would like the program to retrieve: " << endl;
cin >> decider[MAX_CODENAME];
for(int i = 0; i < MAX_SPOTS; i++){
if(decider == league[i].TeamName){
cout << endl;
cout << league[i].TeamName << endl;
break;
}else{
cout << "Searching...." << endl;
}
}
}
I really dont know why its not working but I have included all the perquisite header files such as and but the program crashes when i enter the data and then attempt to search. I get the circle of death and then program not responding then says Process returned 255 (0xFF) . It does not even out put Searching.... the program practically gives up as soon as I enter that name.
Also if this can be optimized by the use of pointers that would be great.
tl;dr run-time error causing the search to fail as soon as i type in a name. And for the record I have checked to make sure the name I entered is valid.
scanf doesn't know about std::string. Use std::cin >> league[i].TeamName.
scanf("%s", league[i].TeamName) ;
This should be changed to
std::cin >> league[i].TeamName ;
A couple of other things here....
string decider[MAX_CODENAME];
cout << "Please enter the team name you would like the program to retrieve: " << endl;
cin >> decider[MAX_CODENAME];
Every time you input a value, you are telling the computer to hold the inputted value at decider[25] but the computer only reads indexes 0-24.
if(decider == league[i].TeamName){
Which array slot are you comparing the team name to? If its the 25th element than the statement should be
if(decider[24] == league[i].TeamName){
Pointers are better suited if the number of TeamNames are unknown. Based on the limited code presented, I highly recommend you stay within the realm of basic data types. For the purposes of troubleshooting, please post your full code in the future.
Your TeamName member variable:
string TeamName[MAX_CODENAME];
is an array of 25 strings, so in this line:
scanf("%s", league[i].TeamName) ;
you are courrupting the array. You don't really want an array anyways, so change the TeamName declaration to:
string TeamName;
and then when you read the name, you'll need to use iostreams which knows how to populate a string type (scanf only works with c char arrays):
std::cin >> league[i].TeamName

Is there a better way to format this while-loop?

While searching for potential errors on a program that I am creating, I got
"[Error] ISO C++ forbids comparison between pointer and integer [-fpermissive]."
The error is coming from my while loop, wherein I intended that the program accepts only the following inputs from the user. Right now, I am considering to use an array for this one.
How should I fix this? If anybody any more details, I am more than happy to provide them by showing what I am (as of late).
int main () // While using one of the three functions to make conversions,
// the program will create the table based on following information.
{
char dimension;
double u1, u2;
int starting_value, ending_value, increment, startingUnitDigits,
endingUnitDigits;
cout << "-------------------------------------------------------------------------------------------------";
cout << "Please answer the following questions as they appear.";
cout << "Check spelling, use correct abbreviation for units of measurement, and avoid negative values.";
cout << "-------------------------------------------------------------------------------------------------";
cout << "Enter the dimensions from the following choices: length, mass and time. (i.e.: 'length,' 'Length').";
while (!(dimension == "length" || dimension == "Length" || dimension == "mass" || dimension == "Mass" || dimension == "time" || dimension == "Time"))
{
cout << "----------------------------------------------------------------------------------------------------";
cout << "Error! Input for Dimension, " << dimension << ", was either an invalid choice or typed incorrectly.";
cout << "Please know that this programs accepts length, mass, and time only!";
cout << "----------------------------------------------------------------------------------------------------";
cout << "Enter the dimensions from the following choices: length, mass and time. (i.e.: 'length,' 'Length').";
cin >> dimension;
The comments are already quite exhaustive, so I just sum them up:
dimension is a single char that you cannot compare to string literals, hence the error. You should use a std::string instead.
Once you changed that the problem remains that you check user input before anything was read from cin. You should do it the other way around.
Your condition will be more readable if you use a container and try to find the user input in that container.
So it would be something like this:
std::vector<std::string> correct_input{"Foo","foo","f00"};
while(true) {
std::string dimension;
std::cin >> dimension;
auto it = std::find(correct_input.begin(),correct_input.end(),dimension);
if (std::cin.fail() || it==correct_input.end()) {
// print error and continue to ask for input
std::cin.clear(); // clear possible error flags
} else {
// do something with input
break;
}
}

How to reject char inputs in cin and define minimum and maximum int values?

I have a program which has the ability to reject user input if a char is entered instead of an int, and this works almost perfectly - anything entered that isn't a number is being rejected.
However, all of these cins need to accept any value between a minimum and a maximum, but I can't get it to work. The code below shows my efforts so far, but there's a slight bug. If a char is entered, followed by an int that is out of range, and another char is entered (I like to test rigorously - I mean, who knows what could happen if an actual end user came across the problem) the program throws the final value of mortgageTerm out as 0.
Could anyone tell me where I'm going wrong and give me any pointers to help me fix it? Thanks in advance to anyone who's able to help me solve my problem!
int mortgageTerm;
string line;
cout << "Mortgage term (1 - 40 years) : ";
while (!(cin >> mortgageTerm))
{
cout << "That's not a valid choice! Try again : ";
cin.clear();
cin.ignore(numeric_limits<streamsize>::max(), '\n');
}
while (getline(cin, line))
{
stringstream linestream;
if (!linestream >> mortgageTerm)
{
cout << "Input was not a number! Try again : ";
cin >> mortgageTerm;
continue;
}
if ((mortgageTerm <= 0 || mortgageTerm > 40))
{
cout << "Input out of range. Try again : ";
cin >> mortgageTerm;
continue;
}
char errorTest;
if (linestream >> errorTest)
{
cout << "Invalid input. Try again : ";
cin >> mortgageTerm;
continue;
}
break;
}
cout << mortgageTerm;
You're almost there. Your first issue is your first while loop is not needed at all. Then we just need to tweak the second loop to make sure that all the input read was used in the value you get. We can also simplify it by using a single error statement, Making those changes gives you
int mortgageTerm;
string line;
cout << "Mortgage term (1 - 40 years) : ";
while (getline(cin, line)) // consume all input given
{
stringstream linestream(line); // you have to construct the stream from the string here
linestream >> mortgageTerm; // try and read the data
if (!linestream.eof() || mortgageTerm <= 0 || mortgageTerm > 40)
{
// either there is input left in linestream or the value is not in range
cout << "Invalid input. Try again : ";
}
}
Just check for the minimum and maximum in the same condition where you check if it was able to be converted into an int, using ||, in a condition the expressions are checked left to right in order, so the first did its work already when you evaluate the second and mortageTerm will have the value.
Edited to address comments.
int mortgageTerm;
cout << "Mortgage term (1 - 40 years) : ";
while (!(cin >> mortgageTerm) ||
mortageTerm < 1 ||
mortgageTerm > 40 )
{
cout << "That's not a valid choice! Try again : ";
cin.clear();
cin.ignore(std::numeric_limits<streamsize>::max(), '\n');
}
// If you are concerned about extra input after the number and want to clear the input stream
// cin.ignore(std::numeric_limits<streamsize>::max(), '\n');