Does this actually ensure that a number is between 1-9? - c++

int position = 0;
void set_position() {
std::cout << "Player " << player << "'s Turn (Enter 1-9): ";
while (!(std::cin >> position)) {
std::cout << "Player " << player << ", please enter a valid number between 1 and 9: ";
std::cin.clear();
std::cin.ignore();
}
std::cout << "\n";
while (board[position-1] != " ") {
std::cout << "Oops, there's already something in that position!\n\n";
std::cout << "Player " << player << "'s Turn (Enter 1-9): ";
std::cin >> position;
std::cout << "\n";
}
}
This is a solution to a tic tac toe challenge in Codecademy and I would like to understand if the function while (!std::cin >> position)) actually checks if it is 1 to 9. Position is just an int, declared as int position = 0;. How does this actually check if it's 1 through 9? Or does it just check if it's an integer? So far Codecademy's C++ course seems to be pretty bad and doesn't go through that much.

Too Long Didn't Read (TLDR)
That code only filters out non-numbers.
Explanation:
To help you understand, lets look at what actually happens inside
while(!(std::cin >> position))
On the inside of all that lies std::cin >> position. >> is the "extraction operator", so we're going to extract something from std::cin (standard input). Using the >> operator really just turns around and calls operator>>() on the given object. The given object in this case is a std::istream. It's operator>>() call signature looks like:
istream& operator>> (int& val)
It returns an istream (itself), and takes a reference to an int, position in your case.
So if we evaluate std::cin >> position mentally (or in a debugger, which is much better: zero guesswork), we'll see that we've returned that istream (which is almost certainly a return *this; at the end of the function).
Now lets replace std::cin >> position with the effective return value std::cin and see what the code looks like.
while(!(std::cin))
Okay, so we need to apply the ! operator to the istream named cin. "Wait what?!" you cry... Any class/struct can define a variety of different operators, such as <<, >>, =, ==, >, <, !, *, ->... there's a bunch of them. The relevant one is the ! operator, with the following function signature and documentation:
bool operator!() const
Returns true if either failbit or badbit is set, and false otherwise. This is equivalent to calling member fail.
Incidentally, the const on the end means "we promise not to change the object running the function". This allows the compiler to make various optimizations, knowing the object's state won't change. It also lets our fellow programmers know what sorts of things are happening (and not happening) inside the function.
We'd expect that this would return true (fail or bad was set) when you type a non-digit into operator>>(int&), and we'd be right. Cheers to the library designers for following the "Principle of Least Surprise".
The return of value of operator!() is then passed into the while loop. So if someone types a letter or punctuation or what have you, they'll see the error message and have to type again.
Conclusion:
This doesn't do anything for filtering out numbers outside of 1-9, it just filters out non-numbers. You'd need new code for that, such as the suggestion in the comment by #Eljay.

Related

How and when to use the getline function to perform calculations?

I am taking a course on C++ and I have been asked to compute the area of a circle by using getline. I was advised to refrain from using cin unless it is really necessary.
Below is my code..
#include <iostream>
#include <string>
int main()
{
std::string question ("Enter the radius: ");
std::string s_radius;
std::cout << question;
getline(std::cin, s_radius);
const double PI = 3.14159;
double n_radius = std::stod(s_radius);
double area = PI * n_radius * n_radius;
std::cout << "The area of the circle = " << area << std::endl;
return 0;
}
Is it really necessary to go through the process of accepting a string as input and convert it to a numeral to perform the calculation?
Is it really necessary to go through the process of accepting a string as input and convert it to a numeral to perform the calculation?
It isn't strictly necessary but it does enforce good habits. Let's assume you are going to ask for 2 numbers from the users and use something like
std::cout << "Enter first number: ";
std::cin >> first_number;
std::cout << "Enter second number: ";
std::cin >> second_number;
For the first number the user enters 123bob. operator>> is going to start reading it in and once it hits b it is going to stop and store 123 into first_number. Now the stream has bob still in it. When it asks for the second number since bob is still in the stream it will fail and second_number will get set to 0. Then the program will continue on and you will get garbage output because you accepted garbage input.
Now, if you read in as a std:string and then convert to what you want it makes it easier to catch these types of errors. getline(std::cin, some_string); would get all of 123bob out of the input buffer so you don't have to worry about having to clean it up. Then using stoi (or any of the stox functions) it will read the valid value of of it. The functions also have a second parameter which is a pointer to a size_t and if you pass it one it will store the position of the first unconverted character in the string and you can use that to tell if the entire input string is valid or not. So, if you had
std::string input;
std::cout << "Enter some number: ";
std::getline(std::cin, input);
std::size_t pos;
double number = std::stod(input, &pos);
if (pos == input.size())
std::cout << "valid input\n";
else
std::cout << "invalid input\n";
Then 1.23bob would cause invalid input to print where 1.23 cause valid input to print. You could even use this in a loop to make sure you get only valid input like
double number;
do
{
std::string input;
std::cout << "Enter some number: ";
std::getline(std::cin, input);
std::size_t pos;
number = std::stod(input, &pos);
if (pos != input.size())
std::cout << "invalid input\n";
else
break;
} while(true)
TL;DR: If you rely on the user to only input valid input eventually you are going to get burned. Reading in as a string and converting offers a consistent way to ensure that you are only getting good input from your user.
I was advised to refrain from using cin unless it is really necessary.
First of all, there is a misunderstanding, because you are still using std::cin. I will interpret your question as using std::cin::operator >> vs getline(std::cin,...). Next I would rather suggest you the opposite: Use operator>> directly if you can and fall back to getline if you have to.
A very contrived example:
Imagine you have integers stored in a file in this (unnecessarily complex) format,
3123212
^-------- number of digits of the first number
^^^----- first number
^---- number of digits of the second number
^^-- second number
You cannot use a bare std::cin >> to read those numbers. Instead you need to read the whole line and then parse the numbers. However, even for this I would highly recommend to provide an overload for operator>>, something along the line of
struct NumbersReadFromStrangeFormat {
std::vector<int> numbers;
};
std::isstream& operator>>(std::istream& in,NumbersReadFromStrangeFormat& num) {
// use getline and parse the numbers from the string
return in;
}
Such that you can again write:
NumbersReadFromStrangeFormat numbers;
std::cin >> numbers;
Conclusion:
Overloading the operator>> is the idomatic way to read something from an input stream. "Using std::cin" or "using getline" are not really alternatives. "Refraining from using std::cin" must be a misunderstanding. If there is no overload of operator>> that already does what you need, you can write your own and nicely hide any getline or other parsing details.

How to understand C++ function/data structs?

'strToDouble' was not declared in this scope Lab1-3.cpp /Lab1-3/src line 65 C/C++ Problem
The first problem, as #SoronelHaetir pointed out, is that you were trying to assign title to variable which can only hold one character. Instead, you should use char array, char pointer, or even string object to contain your multi-letter value. In my code example below, I used char array with fixed size of 25, to store the title. Beware that you can store only up to 24 characters in it, because char arrays need special character which will denote the end of char array. Otherwise it would end up writing junk after your desired value. That special character is null-terminating character which is written like '\0'.
Using return; statement in your void displayBid(Info itemOne); function was completely unnecesary. While you can use return; to stop function from executing, you placed it at the end of function which was just about to end itself in normal way, but you forced it with no reason. Besides, you do not need any return; statements for functions which return void – nothing.
Then, fund and bidAmount are representing money value, which may or may not be of integer value, so you should consider float or double data types to store money value.
Next thing is your function Info getBid();. First, I have to say that naming may be a bit confusing. If you read the name of that function without seeing its actual code, how would you understand what it may do? For me, it sounded like it is about to get me information about a bid, while actually it is setting it up. Second, you could simplify code for entering values, in the way I did it in my code example. The way you tried to use different techniques for getting values from user input was a bit wrong. getline is member function which is used with istream objects. Your istream object is cin. In order to access that member function you shall write it as cin.getline(to be discussed);. That function only works with characters. Its first parameter accepts pointer to the first character (address of the first character) in sequence of characters.
Second parameter is of integer data type and specifies how much characters you want to be extracted from your input and stored in an argument which is in place of the first parameter. Beware not to write, for example, 25, because in char array you have to leave one place for '\0' character, which is automatically placed where it needs to be. getline member function has also default delimiter '\n', which denotes new line. It means that you can enter less characters than function can extract, because extraction will stop as soon as it reads that delimiter value from user input. Although, if you want your specific delimiter, getline member function has its overloaded version which third parameter is one where you enter desired delimiter as an argument. (Overloaded functions are basically functions with the same name, but different parameters. They provide same functionality with different implementation.)
Even if you had set up values for a bid, you never returned it from function. You correctly said that its return value is Info, but you did not return it. Actually, you again exited just before its normal exit. Instead, you should have written return itemOne; In my code example, I passed the variable created in int main(); function by reference, which means it is not a copy as usually, so I do not have to return it and assign to another variable of the same type to appropriately apply desired changes.
Finally, in the int main(); function, you could just declare int choice, without initializing it and use do-while loop in the way I did it. Also, switch statement provides defining what will happen if none of the cases are true, in the way that after all cases you write default:, and below it whatever you want to happen. In your code example, your function will continue executing even if user enters anything but 1, 2 except for 9 defined to stop its execution. In my code example, whatever user enters besides 1 and 2, including zero, function will exit. Well, except for new line.
And, let us discuss again the naming. Your data structure name has to directly imply what it is. Info does not do that. That name would actually be more appropriate for your void displayBid(Info itemOne); function to be called. In my code example, I renamed it to Bid.
#include <iostream>
using namespace std;
struct Bid
{
char title[25];
int vehicleID;
double fund;
double bidAmount;
};
void GetBid(Bid item)
{
cout << "Title: " << item.title << endl;
cout << "Fund: " << item.fund << endl;
cout << "Vehicle: " << item.vehicleID << endl;
cout << "Bid Amount: " << item.bidAmount << endl;
}
void SetBid(Bid & item)
{
cout << "Enter title: ";
cin >> item.title;
cout << "Enter fund: ";
cin >> item.fund;
cout << "Enter vehicle ID: ";
cin >> item.vehicleID;
cout << "Enter amount: ";
cin >> item.bidAmount;
}
int main()
{
Bid item;
int choice;
do {
cout << "Menu:" << endl;
cout << " 1. Enter Bid" << endl;
cout << " 2. Display Bid" << endl;
cout << " 0. Exit" << endl;
cout << "Enter choice: ";
cin >> choice;
switch (choice)
{
case 1:
SetBid(item);
break;
case 2:
GetBid(item);
break;
default:
choice = 0;
cout << "Goodbye." << endl;
break;
}
} while (choice != 0);
return 0;
}
The first (and biggest) problem is:
char title;
This allows you to store only a single character rather than an entire name (prefer std::string to char arrays).

Check if returned value is integer or char

I have a little "blackjack" program coded in C++. The thing is that the program asks questions like "Would you like... (y/n)?" where user needs to type y/n. I want to check if the value returned is actually the type that I want. So function that should return int, returns int and function that should return char, returns char, before it actually returns something.
I would need some suggestions guys. I think it's not that difficult, I just can't find any solution. Thank you.
Code:
char pickCard(){
char aCard;
std::cout << "Would you like another card?";
std::cin >> aCard;
if (aCard is a char){
return aChar;
} else {
std::cout << "Not a char!";
}
}
I think you have a misconception of exactly how std::istream's formatted input works. In your example, aCard must be a char, because you've declared it as such. If the use enters more than one character, one character will be put into aCard, and std::cin will hold onto the other characters and give them to you the next time you call operator>> (or any other input function); if the user enters a number, aCard will be the character representation of the first digit of that number.
Keep in mind that operator>> knows what type of variable you've given it, and it will ensure that the user's input is valid for that type. If you give it an int, it will make sure the users input is convertible to an int or give you 0 if it isn't. A variable can never be anything but the type you declared it to be.
If you're interested in chars in particular, there's a whole bunch of character classification functions you can use to tell what sort of character (letter, digit, whitespace, etc.) you're working with, but keep in mind that char foo('4') is entirely different than int foo(4).
I guess you just want to know if the input string is in number format. >> will set the istream's failbit if it cannot convert the input string to int.
int num;
if (cin >> num) { // input is "123abc", num == 123
cout << "yes, input is a number. At least start with some figures. "
cout << "num == " << num;
} else { // input is "abc" , error
cout << "no, input is not a number or there is some kind of IO error."
}
If you want to force y or n as only allowed characters you can do:
char pickCard(){
std::string response;
std::cout << "Would you like another card?";
std::cin >> response;
if (response=="y" || response=="n"){
return response[0]; //or anything useful
} else {
std::cout << "Not a char!";
//... more code here
}
//... more code here
}
you can use length() property of std::string to find its length. a string of length one is a char in most cases.

How to break out of this for loop: `for (; cin >> A;);`

for (cout << "\nEnter the Sentence now:";
cin >> Ascii;
cout << "The ascii value of each letter you entered, added to the offset factor is: "
<< (int)Ascii + RandomNumberSubtract << endl);
Probably the best advice is don't be clever. Not only do you make it hard for anyone else* to read, understand, and modify your code, you run a real risk of outsmarting yourself.
Thus, don't try to do weird and clever things to implement your loop. Just do things naturally. If they don't naturally fit into how for or while or do ... while statements are structured, then just write a generic loop and use break statements to deal with leaving the loop. e.g.
while (true) {
// Some stuff
if (i_should_break_out_of_the_loop) {
break;
}
// Some more stuff
}
This is pretty much always better than doing things like torturing the for statement in the way you have.
Once you have a clear, easily comprehensible loop, it should be relatively easy to modify it to suit your needs. (or to ask a clearer and more focused question)
*: "anyone else" also includes you three weeks from now, after you've had time for it leave your short term memory.
I strongly advise you to turn this loop into a while loop. However the following is true whether or not you do:
Just enter an EOF, then the loop will terminate.
How an EOF is input will depend on your OS (and possibly also on your terminal settings). On Linux (under default terminal settings) you get an EOF pressing Ctrl+D at the beginning of the line. On Windows, I think it's Ctrl+Z. On Mac I have no idea.
Of course you could also redirect stdin for your program to come from a file (in which case EOF is — as you would guess — generated at the end of file), or from a pipe (in which case EOF is generated as soon as the writing program closes the pipe).
If the variable Ascii is not of type char or string, you may also enter something that cannot be parsed as that variable's data type (e.g. if reading an int, anything other than a number will cause the stream to report failure and thus the loop to terminate).
You also might want to add another end condition then in the loop body (which in your for loop is currently just an empty statement). For example, you might decide that a percent sign should terminate your loop; then you could write (I'm still assuming the type of Ascii which you didn't provide is char):
cout << "\nEnter the Sentence now:";
while(cin >> Ascii)
{
cout << "The ascii value of each letter you entered, added to the offset factor is: "
<< (int)Ascii + RandomNumberSubtract << endl);
if (Ascii == '%')
break;
}
However note that normally operator<< skips whitespace; I guess you don't want whitespace skipped. Therefore you probably shouldn't use operator<< but get; this will also allow you to use the end of line as end condition:
cout << "\nEnter the Sentence now:";
while(std::cin.get(Ascii) && Ascii != '\n')
{
cout << "The ascii value of each letter you entered, added to the offset factor is: "
<< (int)Ascii + RandomNumberSubtract << endl);
}
However in that case, it's better to read the line in one step and then iterate through it:
cout << "\nEnter the Sentence now:";
std::string line;
std::getline(std::cin, line);
for (std::string::iterator it = line.begin; it != line.end(); ++it)
{
cout << "The ascii value of each letter you entered, added to the offset factor is: "
<< (int)*it + RandomNumberSubtract << endl;
}
Note that in C++11, you can simplify this into
cout << "\nEnter the Sentence now:";
std::string line;
std::getline(std::cin, line);
for (auto ch: line)
{
cout << "The ascii value of each letter you entered, added to the offset factor is: "
<< (int)ch + RandomNumberSubtract << endl;
}

string operations and odd results

first post so my apologies if I break protocol.
I'm working on a silly program for school and I have the following code.
cout << "//Dictionary Program//\n\n";
cout << "Enter a command:\n's' - to search for a pattern at the beginning\n";
cout << "'e' - to search for a pattern at the end\n";
cout << "'c' - to search for a pattern anywhere in the word\n";
//Gather user input.
cout << "Please enter a command ('q' to quit): ";
cin >> userCommand;
cmdCheck = userCommand.find("secq");
while(cmdCheck < 0 || userCommand.length() > 1){
cout << "Please enter a valid command\n";
cin >> userCommand;
cmdCheck = userCommand.find("secq");
}
This is driving a menu and I am trying to validate the input. It should be one letter, and one of the following "secq"
I am having a terrible time with the string.find() in the immediate window. I end up with CXX0047: Error: argument list does not match a function. Which I don't understand at all because I am using it elsewhere.
The while condition is not being nice to me. When I give the program a "v" it ends up inside the block like it should, but then I give it an "s" where the cmdCheck should evaluate to 0, but it gives a -1 and stays inside the block.
Lastly, I coded around another error with the cmdCheck but I had that in the while condition and it was not working either. while(userCommand.find("secq") < 0 ...
My inital thought was a problem with the input buffer but when I look at the userCmd variable in the Locals window I have a character array of size 1. There is only the letter and no junk from the buffer (as far as I can tell)
I know I could just tie a bunch of || together with each command but this is a bit more elegant in my opinion. I looked at my final last year and my conditionals were ugly. It's more of a matter of principle at this point.
The expression userCommand.find("secq") tries to find the string "secq" in userCommand. From the sounds of it, you actually want to do the exact opposite, i.e., find the userCommand in the string "secq":
std::string::size_type cmdCheck = std::string("secq").find(userCommand);
while (cmdCheck == std::string::npos) {
...
}
Also note that std::string doesn't return an int. Instead it returns a std::string::size_type. This may be a typedef for int but it may also be a typedef for a different integer type. If the string being passed to find() can't be found, std::string::npos is returned. The exact value for this constant is also not defined so you are best off comparing to this constant instead of making any assumptions.
I'm guessing that userCommand is an std::string. Since the command is supposed to be a single character, use a char instead of a string. Then just use the value as the argument in a switch statement, with appropriate cases for the valid characters and a default case that gives an error message.
Take input using getline in a string.
getline (cin, userCommand) ;
If the input is one letter, take it in a single char. If you insist on taking it in a string, use its first index to check.
Maybe a loop like this would be more appropriate:
char result;
std::cout << "Your command: ";
for (std::string line; ; )
{
if (!(std::getline(std::cin, line))
{
std::cerr << "Fatal error: Unexpected end of input!\n";
std::exit(1);
}
if (line.size() == 1 && line.find_first_of("secq") == 0)
{
result = line[0];
break;
}
std::cout << "Sorry, I did not understand. Please say again: ";
}
std::cout << "Thank you! You said, '" << result << "'\n";
Now if the loop breaks, result will contain the user input.