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

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;
}
}

Related

Is there a way to access protected vector size via main function without turning it to public?

I've been working on a Shape program lately ( Some of you might remember my other questions about this... ;/ ) And I have a tiny problem which I want to fix.
In my Menu class, which holds all the functions related to the menu. I have a unique_ptr vector with the type of my base class Shape which holds all of the newly created objects ( Circles, Rectangles, ect )
protected:
vector<unique_ptr<Shape>> _shapes;
One of the functions that I want to create is supposed to change the values of the variables in a given shape based on it's index. To do so, I was planning to print the vector to the user, and then let him to choose the index of the shape that he wants to change.
void Menu::printShapes() const
{
int i = 0;
for (auto p = _shapes.begin(); p != _shapes.end(); p++, i++)
{
cout << i + " ";
(*p)->printDetails();
cout << endl;
}
}
The problems lays in my main program which is going to use my Menu functions. Because I don't want the user to be able to enter values which are outside of my vector, I have to check if the given input is between 0 and the size of the vector. But I cannot access this info from my main function without making the vector public or make a return statement from the printShapes() function, which will make the code messy and not intuitive as I want it to be.
So my question is: Is there a way to find the size of the vector at the Menu function from the main function without making the changes I stated above? Because in the end I want to be able to just do menu.printShapes() and then let the user to choose the index of the shape that he wants to change
this is my main function as of now:
Menu menu;
int input = 0, wait = 0;
while (input != 4)
{
cout << "1: Add New Shape: " << endl;
cout << "2: Modify Existing Shape: " << endl;
cout << "3: Delete All Shapes: " << endl;
cout << "4: Exit: " << endl;
while (input < MIN || input > MAX)
{
cin >> input;
cin.ignore(numeric_limits<streamsize>::max(), '\n');
std::cin >> wait;
}
switch (input)
{
case 1:
{
cout << "1: Circle: " << endl;
cout << "2: Rectangle: " << endl;
cout << "3: Triangle: " << endl;
cout << "4: Arrow: " << endl;
while (input < MIN || input > MAX)
{
cin >> input;
cin.ignore(numeric_limits<streamsize>::max(), '\n');
std::cin >> wait;
}
menu.createShape(input);
}
case 2:
{
/*
I want to be able to access the size of the vector from here
So I could do something like that:
menu.printShapes();
while (input < 0 || input > vectorSize)
{
:Get the index of the shape that the user wants to modify
}
Instant of doing
size = menu.printShapes();
*/
}
}
}
You're way overthinking this.
You just need to add a public function that returns the current size of the vector:
// Declaration in your class
size_t numberOfShapes() const;
// Definition
size_t Menu::numberOfShapes() const
{
return _shapes.size();
}
. Then, when you want to know the size, call that function.
menu.printShapes();
while (input < 0 || input > menu.numberOfShapes())
{
// Get the index of the shape that the user wants to modify
}
Simple!
By the way, I think you meant >= there, not >.
You have at least a few options; depending on the details of the modify-existing-shape operation.
The easiest thing would probably be to have your Menu type expose a method to tell you how many shapes it's managing. I agree you shouldn't make the vector public because the program doesn't need to know it's a vector specially, but if "knowing how many existing shapes the menu is managing" is a requirement (which it seems to be) then exposing it seems reasonable.
Alternatively, depending on the return type of the modify operation you could return an optional or a variant. If you have 7 shapes and I ask you to modify the 6th then you might tell me that it worked or maybe the new dimensions, but if I ask you to modify the 9th you might tell me it's an invalid index.
The difference becomes whether to obligate the caller to be informed and ask a valid question, or whether you want to be more robust and answer and handle a broader domain of questions. I don't think it makes a big difference in this case but I tend toward the second kind of solution simply because it means any potential caller is protected from going out of range instead of all of then having to check the count then do their own validation.

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

Issue with a for loop (C++, Visual Studio [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 years ago.
Improve this question
I have only really been coding for a few days, though I've reading the textbook for my Intro to C++ class for two weeks. I'm having an issue with an assignment, and I feel like I'm missing something super simple, but I can't understand what I've done wrong.
The exercise calls for you to 'write a program that reads numbers from cin, and then sums them, stopping when 0 has been entered.'
The professor told us we could write it with a for loop, a while loop, or a do-while loop. I am trying to write it with a for loop.
The program compiles successfully, it allows me to enter multiple values, and it sums it correctly. It also stops on no successfully. The only thing that's wrong with it is when I enter 0, it does not stop the program. I have tried using different commands inside the for loop, such as goto, and trying to direct it to go to break; when the value entered is zero, but my knowledge is shoddy, to say the least. I've read the textbook but I don't have enough experience yet, and I don't remember everything, and I can't figure out what I'm doing wrong.
This is what the program looks like:
#include <iostream>
using std::cin;
using std::cout;
using std::endl;
int main()
{
char indicator{ 'n' };
double value{};
double sum{};
for (;;)
{
cout << endl
<< "Enter a value here: ";
cin >> value;
sum += value;
cout << endl
<< "Do you want to enter another value (enter y or n)? ";
cin >> indicator;
if (('n' == indicator) || ('N' == indicator))
break;
}
cout << endl
<< "The sum of the values you entered is " << sum << "."
<< endl;
return 0;
}
Please point out my stupid mistake, I'd be grateful to learn. Thank you!
There is no sense to ask the user each time whether he wants to continue.
So I would write the loop the following way
cout << "Enter a sequence of real numbers (0 - exit): ";
for (;;)
{
if ( !( cin >> value ) || ( value == 0.0 ) ) break;
sum += value;
}
Also as the variable value is used only in the body of the loop it should be declared there. So the loop can look like
cout << "Enter a sequence of real numbers (0 - exit): ";
for (;;)
{
double value;
if ( !( cin >> value ) || ( value == 0.0 ) ) break;
sum += value;
}
An alternative for this for loop is while loop of the following form
cout << "Enter a sequence of real numbers (0 - exit): ";
while ( true )
{
double value;
if ( !( cin >> value ) || ( value == 0.0 ) ) break;
sum += value;
}
Just compare value to zero after it is inputted:
for (;;)
{
cout << endl
<< "Enter a value here; enter 0 to stop: ";
cin >> value;
if(value==0.0) break;
sum += value;
}
I would like to make a few suggestions to you which you may find helpful.
To begin, any of the loops you mentioned (for, while, do while) may be used in solving this problem. But I believe that the do while lends itself best to this particular problem.
The use of a for is especially bad for this exercise as for loops are typically employed while performing iterations over a set of values or some sort of finite counting. Being asked to perform an action an indefinite number of times is better suited for a while or do while.
Just look at your for declaration:
for(;;)
{
// ...
}
What benefit does using for have in this situation if you make use of none of it's functionality.
Second, the use of double is for your value and sum is not recommended either. This is because of something called floating-point precision. You can not reliably compare a double value using ==. See: What is the most effective way for float and double comparison?
Instead, unless specified by the problem statement, I would opt to use an integer value type. Either int or unsigned int.
Third, you are not correctly initializing your variables. Instead, it should be done as:
int value = 0;
int sum = 0;
Improper variable initialization can lead to countless bugs and is a very common source of problems.
Finally, I would recommend against the use of using namespace. This is used commonly by beginner developers who then are taught better practices and then have to unlearn the behavior. See: Why is "using namespace std" considered bad practice?
Taking this advice into consideration would lead to a solution such as:
#include <iostream>
int main(int argc, char** argv)
{
int sum = 0;
int value = 0;
do
{
sum += value;
std::cout << "Please enter a value: ";
std::cin >> value;
} while(value != 0);
std::cout << "The sum of all values entered is: " << sum;
return 0;
}
ITNOA
You can just change
cout << endl
<< "Do you want to enter another value (enter y or n)? ";
cin >> indicator;
if (('n' == indicator) || ('N' == indicator))
break;
with
cout << endl
<< "Do you want to enter another value (enter 0 for exit or not zero to continue)? ";
int indicator = -1;
cin >> indicator;
if (indicator == 0)
break;
If you do not want to ask user to continue enter new value in every time #Govind Parmar and #Vlad from Moscow write cleaner code than you write.

Recipe to nutrition converter

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.

C++ Beginner - Best way to read 3 consecutive values from the command line?

I am writing a text-based Scrabble implementation for a college project.
The specification states that the user's position input must be read from single line, like this:
Coordinates of the word's first letter and orientation (<A – P> <1 – 15> <H ou V>): G 5 H
G 5 H is the user's input for that particular example. The order, as shown, must be char int char.
What is the best way to read the user's input?
cin >> row >> column >> orientation will cause crashes if the user screws up.
A getline and a subsequent string parser are a valid solution, but represent a bit of work.
Is there another, better, way to do this, that I am missing?
Thanks for your time!
getline and parsing doesn't necessarily have to add much work. Since you already know how to read (correct) data from a stream, just read a line with getline, then create an istringstream from the line and read from there.
The one thing I'd add would be that it might very well make sense to create a class to hold the data for a particular move, and overload operator>> for that class to read data for a move. A rough sketch would be something like this:
class move {
char row;
int column;
char orientation;
public:
friend std::istream &operator>>(std::istream &is, move &m);
};
std::istream &operator>>(std::istream &is, move &m) {
std::string temp;
std::getline(is, temp);
std::istringstream buffer(temp);
// Now we can parse data from buffer just like we could from another stream.
return is;
}
At least for the moment, I haven't included any error handling code. Depending on how picky you want to be, this can get a little tricky (e.g., if input from the stringstream fails, setting the fail bit in the original input stream).
Sorry, but getline and parsing the string are your best bet. However, you can make your system a little bit more reusable by creating a class to represent the input options and then overloading operator>> so that it uses getline and parses the string. That way, you don't have to repeat any of your parsing code.
I got something like this:
#include <iostream>
#include <limits>
#include <string>
using namespace std;
template<class T> T getValue(const string& name)
{
T ret;
while(!(cin >> ret))
{
// normally here you'd go into an infinite loop, but since you're going to ignore the rest of the line, you can ensure that you won't go into an infinite loop and you can re-ask the user to input the correct data
cout << "Invalid input for " << name << " please try again" << endl;
cin.clear();
cin.ignore(numeric_limits<streamsize>::max(), '\n');
}
return ret;
}
int main(void)
{
bool valid = false;
char row, orientation;
int column;
do {
cout << "Enter row, column, and orientation (<A-P> <1-15> <H to V>): " << endl;
row = getValue<char>("row");
column = getValue<int>("column");
orientation = getValue<char>("orientation");
if(row<'A' || row>'P')
cout << "Invalid row please enter A-P" << endl;
else if(column<1 || column>15)
cout << "Invalid column please enter 1-15" << endl;
else if(orientation<'H' || orientation>'V')
cout << "Invalid orientation please enter H-V" << endl;
else
valid = true;
} while(!valid);
cout << "Row: " << row << endl
<< "Column: " << column << endl
<< "Orientation: " << orientation << endl;
return 0;
}
Of course, if you enter something invalid like:
A B C
It would produce some potentially confusing problems. The first A would be successfully copied in row char variable. However since B is not numerical, it would ignore the remaining buffer, so you lose B and C. You get an error message that you entered an invalid input for column, but once you successfully enter a valid number, you would still have to enter a orientation again. So the user isn't clear on that point based on this application. You can make such modifications easily such that if you enter an invalid input, it would reask you to enter the whole thing.
Another suggestion is to input from the console, using one item at a time and apply an error checking:
char row;
bool is_valid = false;
while (!is_valid)
{
while (!(cin >> row))
{
cout << "Error reading row, please enter data again.\n";
}
row = toupper(row);
static const std::string valid_rows("ABCDEFGHIJKLMNO");
is_valid = valid_rows.find(row) != std::string::npos;
if (!is_valid)
{
cout << 'Row ' << row << ' is not a valid row letter, please re-enter.\n";
}
}
By reading one variable at a time, rather than all three at once, you can give the user earlier warning about error detection.