Failure on Input Response

I am adding some quality checks to the sample program for a simple calculator example. The check for divide by zero was included. I added a check for a proper operator, +,-,/ or *. Now I am trying to gracefully handle a character input where a number should be. It compiles and runs but when I test it, it just repeats the last output until I close the app. How do I get it to just restate the initial Input request??
[code]
Put the code you need help with here.

cout << "Calculator Console Application" << endl << endl;
cout << "Please enter operation to perform. Format:a+b | a-b | a*b | a/b"
<< endl;

Calculator c;
TestInputs t;

while (true)
{
cin >> x >> oper >> y;

if (cin.fail())
{
cout << "Input must be a number \n";

continue; // This command is where I am having trouble.
// I want it go to input
// request line above If input fails, say a letter is input for x or y.

}
Last edited on
Hello SJSwanson,


PLEASE ALWAYS USE CODE TAGS (the <> formatting button), to the right of this box, when posting code.

Along with the proper indenting it makes it easier to read your code and also easier to respond to your post.

http://www.cplusplus.com/articles/jEywvCM9/
http://www.cplusplus.com/articles/z13hAqkS/

Hint: You can edit your post, highlight your code and press the <> formatting button.
You can use the preview button at the bottom to see how it looks.

I found the second link to be the most help.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
cout << "Calculator Console Application" << endl << endl;
//cout << "Please enter operation to perform. Format:a+b | a-b | a*b | a/b"
//<< endl;
std::cout << "Please enter operation to perform. Format: a+b or a-b or a*b or a/b: ";

Calculator c;
TestInputs t;

cin >> num1;

while (!std::cin)
{
	std::cout << "Input must be a number \n";

	std::cin.clear();
	std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');  // <--- Requires header file <limits>.

	std::cout << "Enter first number again: ";
	std::cin >> num1;
}

std::cout << "Enter operator: ";
std::cin >> oper;

std::cout << "Enter second number: ";
std::cin >> num2;

while (!std::cin)
{
	std::cout << "Input must be a number \n";

	std::cin.clear();
	std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');  // <--- Requires header file <limits>.

	std::cout << "Enter second number again: ";
	std::cin >> num2;
}

//  Using what you started with. The problem is that you do not know which number is wrong.
std::cin >> num1 >> oper >> num2;

while (!std::cin)
{
	std::cout << "Input must be a number \n";

	std::cin.clear();
	std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');  // <--- Requires header file <limits>.

	std::cout << "Please enter operation to perform. Format: a+b or a-b or a*b or a/b: ";
	std::cin >> num1 >> oper >> num2;
}

I changed the first "cout" statement. Not everyone running the program may understand what the "|" means. And unless the prompt is long I find it better to end the line with just a ";" putting the "cin" on the same line.

The code shows two choices. The first possibility, lines 9 - 37 allow you do deal with each number and operand separately.

Lines 40 - 51 use what you started with, but you do not know what number caused the problem and would have to reenter everything again.

Choose one set or the other, but do not try to use both in the same program.

This may be a short program and easy for you to keep tack of variable names, but do yourself a favor, and for those reading your code, and use a variable that better describes what it is. "num1" and "num2" are just one possible choice yet you can see that it is better than "x" and "y". In the end variable names are your choice, but you will find the better the name the easier it is to work with in the code.

One last note: It is helpful to include enough of the program so that it can be compiled and tested. The above code is not tested, but should work, because I have not yet set up a program that will compile and run for testing.

Another point if (cin.fail()) the ".fail" function only checks one of the bits and the "fail" bit may not always be set if there is a problem. By using "!std::cin" it does not matter which of the bits are set it will catch any bit that was set.

Hope that helps,

Andy
Andy - Your comments are greatly appreciated. Indeed, your code works perfectly. I also appreciate your advice and coaching. This is my first post.

I am using Visual Studio and I zipped all the files in the project so you can compile it but I don't know how to get the zip file to you. I cannot attach is here.

Your help on my first project is greatly appreciated. I took the example code for "Calculator Tutorial" and added some features as my first learning experience.

Let me know if there is a way to send you the zip file.

Steve
Let me know if there is a way to send you the zip file.

People usually upload the file to a file sharing service (such as Google Drive) and then post a shareable link.
Hello SJSwanson,

In addition to dutch's suggestion I have seen people use https://pastebin.com/

You could try clicking on my name and send me a private message. Not sure if I would be able to respond to it, but it might work.

Andy
Thanks for the advice. Here's a link so Andy and others can see the entire project in Visual Studio.

https://www.dropbox.com/transfer/AAAAABeYmHgLMIgE1lKvgYZEESJSmRbHlK_wxTz76-vWX93WCUCZYNE
Windows messed up calc so badly I wrote my own and use that now. Looks like you are off to a good start. Mine is reverse polish though.
Hello SJSwanson,

The "dropbox" worked, but there was more information than is needed.

One piece of information that is missing is what version of VS are you using? I makes a difference.

I use VS 2017 and some of the files may not be compatible with earlier versions.

A tip: all you need is the ".cpp" and ".h" files not everything else. Not everyone uses VS as their IDE and the files ".sln", ".vcxproj", "vcxproj fileterd" and ".vcxproj user" files. Also I believe that these files are designed and built for your computer and directory structure. My past attempts to use something old have resulted in failure.

You can save yourself some work by dealing with only the relevant files that comprise the program and can compile the program.

After I figured out which "main" file I should use I noticed some things:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// CalculatorTutorialMod1.cpp : This file contains the 'main' function. Program execution begins and ends there.
//

#include <iostream>
//#include <stdio.h> // <--- Not needed. Covered by "iostream"
#include <stdlib.h>  // exit, EXIT_FAILURE. With VS this is included through "iostream". OK to leave, but not necessary to have.
#include <string>    // <--- Always a good one to have this one and you can see how I used it in the first "cout" statement.

#include "CalculatorMod1.h"

using namespace std;  // <--- Best not to use.
// The most recent post that is worth reading. http://www.cplusplus.com/forum/beginner/258335/

int main()
{
    constexpr int WIDTH{ 70 };

    double x = 0.0;
    double y = 0.0;
    double result = 0.0;
    char testresult = 'Pass';  // Test Code
    char oper = '+';
    std::string heading{ "Calculator Console Application" };  // <--- Added.

    std::cout << '\n' << std::string((WIDTH / 2) - (heading.size() / 2), ' ') << heading << '\n'
		<< std::string(WIDTH, '-') << '\n' << endl;
    //cout << "Please enter operation to perform. Format:a+b / a-b / a*b / a/b"
    //    << endl<<"> ";
    std::cout << " Please enter operation to perform.\n Format: (a+b / a-b / a*b / a/b): ";

The comments in the program should explain things. If not let me know.

I added line 23, so I could use the ".size" function in line 25.

Line 21 is a problem char testresult defines a single character of type "char". What you have is char testresult = 'Pass';. When compiled it tells me:

Warning	C4309	'initializing': truncation of constant value	20	
Warning	C4305	'initializing': truncation from 'int' to 'char'	20
Warning	C4244	'=': conversion from 'double' to 'char', possible loss of data	37	


This is cut down an bit and "Warning" is a yellow triangle with an "!" in it. The first one tells me that everything after "p" is being dropped. Not sure about the second warning, but I believe it goes with the first one.

You can initialize the variable with just 'P' or change "testresult" to an array char testresult[5] = "Pass";, but this creates other problems, but this creates other problems. The best choice is to use a "std::string" which is easier to work with.

The last warning goes with testresult = t.Test(x, oper, y);. The "testresult" is defined as a "char", but the "t.Test" function returns a "double". This will not fit in a "char".

Since these are only warnings the program still runs and appears to work for addition. I have not tested out the other operators yet.

1
2
3
testresult = t.Test(x, oper, y)_      // Test Code

if _testresult != 'pass'_             // Test Code 

I used the underscores to help point out what is missing.

In line 1 "testresult" has the value of "\0" when finished. Because you can not store a "double" in a "char".

In the if statement the condition will always be true because you are comparing a "char" to an "int". When the program runs I get this: 'pass' = 1885434739. Not quite what you want.

This is the first time I have dealt with something like this, so I am not sure how the compiler is dealing with it.

I think you have a misunderstanding here. The single quotes are for a single character. The double quotes are for strings. In the if statement you are trying to use single quotes to define a string. These should be double quotes, but that would leave you comparing a "char" to a "string".

After you get by the if statement the line cout << testresult << endl; is printing a blank line because "testresult" has the value of ('\0') which means there is nothing to print except the "endl".

The last two lines of the while loop are OK, but I would probably change the "cout" statement. I will show you an idea when I get to that point.

The only other parts I changed are the two header files. The #pragma once is usable, but not everyone can use it. The better option is to use include guards. I am combiling both files here, but in the solution they are separate.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

#ifndef CLASSES
#define CLASSES

class Calculator
{
public:
	double Calculate(double x, char oper, double y);
};

class TestInputs
{
public:
	double Test(double x, char oper, double y);
};
#endif // !CLASSES

#ifndef CALCULATOR
#define CALCULATOR

class Calculator
{
public:
	double Calculate(double x, char oper, double y);
};

#endif // !CALCULATOR

Not what I had in mind, but it does work.

The parts in bold make up the include guard. The names can be anything that you want. Most often I have seen these names in capital letters, but this in not required as far as I know. And if you have more than one work I tend to use the underscore to separate them.

Two last points:

The while loop in "main" is an endless loop with no way out.

When it comes to writing variable names the choice is yours to make, but when a variable name is two or more words the "camalCase" form is more often used, so "testresult" would become "testResult". You may find it does make it easier to read in the code.

Along with that try to avoid using single letter variable names. They may mean something to you when writing the program, but not to others. Also in the future when you look back to this program you may not remember what the single letters are used for when you first look at the program. A suggestion "num1" for "x" and "num2" for "y". The have a bit more meaning than the "x" and "y". This also makes the program easier to follow.

Hope that helps,

Andy

Andy -

You went WAY above and beyond what I expected. You can tell I am a real beginner.

I am using VS 2019. I will remember in the future only the header and .CPP files need to be sent.

I am going to spend some time studying ALL you comments - MUCH appreciated. I will also read the article you referred me to about "Using namespace std;"

Thanks!!

Steve
Andy -

https://www.dropbox.com/transfer/AAAAAIkbTJMYFyoZF8r4BqHlJTFxNxKBZBa2vw_d5rXlBWEk5M6pqk8

This is the link to the updated version.

I took most of your suggestions even though I don't (yet) understand them. I fixed the "testresult" issue by making it a Boolean. I will fix my naming convention in the future for testresult (testResukt) and the Num1, Num2 format as I progress. I left the "using namespace std" for this small project but I can see how large projects it could cause a problem. I read the referenceyou provided.

You are right, there is no graceful exit option. I wish I could close the window by typing "exit" but I haven't figured out how to do that yet.

Again, I really appreciate all your help and coaching. See how this next version looks.

Steve
Hello Steve,

Sorry about this, but sometimes I tent to go to far in explaining things and this became larger then is allowed or a message. For now to keep the formatting that I have used I wil have to break this up into at lease two messages.

I took most of your suggestions even though I don't (yet) understand them.

If you do not tell me what you do not understand I can not guess at what to explain, but I will try to cover some of what you might not understand.

I left the "using namespace std" for this small project but I can see how large projects it could cause a problem.

That is alright for now, but at the same time it is good practice so that you learn slowly instead of all at once.Either way it it your choice.

Now for the program.
1
2
3
4
5
6
7
8
9
10
11
#include <cctype>  // <--- Added. For std::tolower() and std::toupper().
#include <iostream>
#include <iomanip>  // <--- Added.
#include <limits>
#include <string>

//#include <stdbool.h> // <--- Nothing used from this header file.
//#include <cstdio> // <--- Not needed. Covered by "iostream"
#include <cstdlib>  // exit, EXIT_FAILURE. With VS this is included through "iostream".

#include "CalculatorMod1.h" 

The first group of header files are what I have found to be the most used in any program.The order does not make any difference, but I found, quite by accident, the the alphabetical order does help if you miss one.The error messages also help to draw your attention to these include lines to check what you have missed.

The second group are header files that are specific to the program.Again the order does not make any difference just that they are there.

I use the third group for any header file that is in double quotes.

Recently it has been said that a file like "CalculatorMod1.h" should be the first "#include" and this may be true, but my point of view is that by making it last all the above "includes" will cover anything in the header file that would need them.I have found that this does tend to work well.

This is only my option and does not mean that you have to follow it.You are free to do what works for you.

The next part that you will notice are the header files "stdio.h" and "stdlib.h".These are C header files designed for a C program.C++ has the header files "cstdio", "cstdlib", "cmath", "ctime", "cctype" and "cstring".Some of these you are not ready to use yet, but the "c????" header file are the better files to use.It ia also not a good idea to mix C header files with C++ header files.

The "stdbool.h" header file in my VS2017 :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//      Copyright (c) Microsoft Corporation. All rights reserved.
//
// The C Standard Library <stdbool.h> header.
//
#ifndef _STDBOOL
#define _STDBOOL

#define __bool_true_false_are_defined	1

#ifndef __cplusplus

#define bool	_Bool
#define false	0
#define true	1

#endif /* __cplusplus */

#endif /* _STDBOOL */

/*
 * Copyright (c) 1992-2010 by P.J. Plauger.  ALL RIGHTS RESERVED.
 * Consult your license regarding permissions and restrictions.
V5.30:0009 */

When you look at line 10 "__cplusplus" has already been defined, so the next three lines are, in a sense, grayed out and not included in the program.

Data type like "bool", "char", "short", "int", "long and long long", "float" and "double" are basic data types that, as I understand it, are part of the IDE and available for use with out having to include some special header file.I could be a bit off on this and the IDE may include a header file at compile time that you never see.Just a thought.

As I said earlier "cstdlib" is included through "iostream".It was this way when I used VS2015 and again with VS2017.Which is why I never think about including this header file.It has also been said that if you think you need a header file include it.The other way to look at this is that not everyone used some version of VS and will need this header file.As I think about I am believing it is more prudent to include "cstdlib" for others who might need it.

For the "main" function:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
int main()
{
	const std::string heading{ "Calculator Console Application" };
	constexpr size_t WIDTH{ 70 };

	bool testResult{}, cont{ true };  // Test Code
	char oper = '+';
	char choice{};
	double num1{};
	double num2{};
	double result{};

	std::cout << std::fixed << std::setprecision(4); // <--- Only needsdone once.

	std::cout << '\n' << std::string((WIDTH / 2) - (heading.size() / 2), ' ') << heading << '\n'
		<< std::string(WIDTH, '-') << /*'\n' <<*/ std::endl;
	//cout << "Please enter operation to perform. Format:a+b / a-b / a*b / a/b"
	//    << endl<<"> ";

	Calculator calculate;
	TestInputs testOperator;                                  // Test Code

	while (cont)
	{
		std::cout << "\n Please enter operation to perform.\n Format: (a+b / a-b / a*b / a/b): ";
		std::cin >> num1 >> oper >> num2;

		while (!std::cin)
		{
			std::cerr << "\n    Invalid entry! Numbers only.\n";

			std::cin.clear();
			std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');  // <--- Requires header file <limits>.

			std::cout << "\n Please enter operation to perform. Format: a+b or a-b or a*b or a/b: ";
			std::cin >> num1 >> oper >> num2;
		}

		if (oper == '/' && num2 == 0)
		{
			std::cout << "\n    Division by Zero Exception\n" << std::endl;

			continue;
		}

		testResult = testOperator.Test(oper);          // Test Code  // <--- Changed.

		if (testResult)                     // Test Code  // <--- Changed.
		{
			std::cout << "\n num1 = " << num1 << std::endl;             // Test Code
			std::cout << " oper = " << oper << std::endl;       // Test Code
			std::cout << " num2 = " << num2 << std::endl;             // Test Code
		}

		//cout << testResult << endl;        // Test Code // <--- Used for testing?

		result = calculate.Calculate(num1, oper, num2);

		std::cout << "\n Result is: " << result << std::endl;

		std::cout << "\n Do another? (Y/N): ";
		std::cin >> choice;

		if (std::toupper(choice) != 'Y')
			cont = false;
	}  //  End while loop.

	return 0;
}  //  End "main".  


End part 1
This part refers to the code at the end of the first part.

The first two lines are constant variables.I like to put these at the top of a function where they are easy to find.Should I need them as global variables I would put them after the "#include"s.

The "constexpr" became available with the C++11 standards as did the uniform initializer of the{}s.For me I am trying to learn and use the more updated version of things that changed with the C++11 standards.The only problem I am having is the "constexpr" does not work with "std::strings" and am not sure why or what I am doing wrong, so for not I use "const std::string" until I figure out what I am doing wrong.

After that you define your variables, note I have added some.Not all variables may need to be initialized, but I find, if only for the peace of mind of knowing that they have a value and not the garbage that was left in memory when the variable was created, along with the uniform initializer it makes it very easy.Some have said that you should always initialize your variables.

With the uniform initializer(the{}s) if they are empty it will set most numeric variables to(0) zero.What you actually get is "bool" is set to(0)zero or "false".A "char" is set to "\0". "int" type variables are set to(0) and floating point types, (float and double), are set to(0.0).

The exceptions are "std::string"s, "std::vector"s, "std::list"s and other containers are empty when defined and have no size, so they do not need initialized unless you want to give them some value.

The other advantage is that you can put something between the{}s to give the variable a value.You can see how I have done this in the code.

When the day comes and you use a C style array, something like, int nums[MAXSIZE]{}; .This will initialize the first element of the array and every element that is left.

Very handy to get use to using.

Line 13 first requires the header "iomanip" to work.As the name implies these are manipulators that work with "iostream" and the "cout" and "cin" statements to define what is displayed on the screen.When using "double"s in a program this becomes useful because the default is "scientific notation" and printing only 6 numbers.

The "std::fixed" says to use numbers not "scientific notation" and the "std::setprecision(4)" says to always print 4 decimal places.Otherwise as the whole number increased in digits the decimal portion decreases in size.As you progress with your programs this will make more sense in the future.

The next part of "iomanip" you are likely to use is "std::setw()".Not really necessary for this program, but could be used.

Line 15 that prints the headings.The first new line is optional.For me putting the first line off the top edge of the screen makes it easier for me to read.And IMO makes the heading more presentable.

The first "std::string()" is designed to center the heading based on the length of the second "std::string()".This is why I defined the variable "WIDTH" so you have only one place to change the size of the heading and the line that prints under the heading.In this program heading has an even number of characters, so I would keep "WIDTH" an even number.

I do tend to realize that this may not be taught in a class or even in the book you are using.Have a look at http ://www.cplusplus.com/reference/string/string/string/ You will see that the first parameter is of type "size_t". This is an alias for "unsigned int". You will find class member functions like "str.size()" or ".length()" return a "size_t" type. "str" would be defined as a "std::string". If you want more information on "size_t" let me know and I will cover that.

In the first "std::string()" that big formula is just calculating how many spaces are needed to center the heading based on the value of "WIDTH".The second parameter is what to fill the string with, in this case spaces.

"heading << '\n'" should be easy enough to understand.

The second "std::string()" uses "WIDTH" as the size of the string and in this case you are filling the string with the minus sign.Although you are free to use anything you like here.

In each case, and if I understand this correctly, "std::string()" creates a temporary variable, that you do not see, to use with the "cout" statement.

I changed the outer while loop. The code at the end should be easy enough to understand. The "std::toupper(choice)" will change the case of the variable if needed. If it is in the proper case nothing happens. The other option would be if (choice == 'y' || choice == 'Y').

I moved the prompt from outside the while to inside so it prints each time before the "cin" statement.

The next while loop while (!std::cin) you missed the message that something is wrong. May be not really necessary, but it does let the user know that something went wrong. Lately I have found that indenting the message with, I use 4 spaces, sets it off when the user sees it. Something that time and experience has taught me.

After some test I found the the ofder of the if statements needed reversed.

The rest of the outer while loop is OK down to what I added.

For the rest of the program.

You have created classes for your functions. Unless this was done for practice and learning about classed they are not needed. you can just as easily make your functions regular functions. I did create a program this way and it works fine as best as I have found os far.

Your bigest problem is with the variable "testResult" and the function "TestInputs::Test". It does not matter how you define "testResult", as a "char" or a "bool" the function returns a "double" and trying to store a "double" in a "char" or a "bool" is the problem.

I solved this by changing the function:
1
2
3
4
5
6
7
8
9
10
bool TestInputs::Test(char oper)
{
	if ((oper != '/') && (oper != '*') && (oper != '+') && (oper != '-'))
	{
		cout << "Error on Operator Input \n";
		return false;
	}

	return true;
}

Now the return type of the function matches the type for "testResult".

For the line if (testResult) what is in the () will evaluate to "true" or "false". Since "testResult" is a bool varaible your only two choices are "true" or "false". So you can simply say[code]if (testResult)[/ code]. If "testResult" is "true" you enter the while loop and if "false" you bypass the if statement. It is a little trick to save some time and make your life easier.

Hope that helps,

Andy
Andy - I really can't thank you enough for all the time you are spending helping and coaching me. It really is useful and I study ALL of it. I use the CPlusPlus.com website to search for definitions and such.

You asked what I didn't understand. I haven't yet figured out the follow code you sent me. The rest I have sorted through and implemented your changes and made some improvements. Here's what I haven't figured out yet..

" while (!cin)
{
cin.clear();
cin.ignore(numeric_limits<streamsize>::max(), '\n'); // <--- Requires header file <limits>."

I am just not familiar with the <limits> functions and some of the nomenclature like "!cin" (I figure it means "not cin") but I'm not familiar with that just like "cin.clear and cin.ignore". I'm sure these are just basic C or C++ commands but I haven't seen or used them. I am REALLY new at programming and am basically, teaching myself with simple examples to get started until I find a class to take. I took a C programming class several years ago and MANY years ago I was fluent in FortranIV. I wrote a 1000 line program that simulated multicomponent distillation in a trayed distillation column. Right now, I am learning C++ vocabulary and punctuation! It is a BIG language!!

I will study your feedback carefully and thoroughly and get back to you with any further questions or with the next version!!

Thanks again for your time and effort.

Steve

PS: My ultimate goal is to write a program that will solve Sudoku puzzles!
Last edited on
Hello Steve,

To answer your question you first need to understand the streams. The stream could be "cin", "cout" or "inFile", where "inFile" is defined using "fstream", to use an external file. All have 4 bits to keep track of the state of the stream. These are the "bad", "fail", "eof" and "good" bits. The first three are generally referred to as the bad bits and would be set to (0) zero. The good bit is set to (1) when the stream is working.

When I need to look at what I do not remember I end up having to hunt for this table http://www.cplusplus.com/reference/ios/ios/bad/#description

The next part is the formatted input std::cin >> number;. What this means is that "cin" understands that "number" is defined as an "int" or "double" and expects what you type in to be a number. Anything else will cause "cin" to fail either setting the "fail" bit or the "bad" bit which also sets the "fail" bit. This also changes the "good" bit to (0) zero. Thus making the stream unusable until it is fixed.

I have found that using formatted input with a numeric variable has the most advantage. Inputting to a "char" or a string is less likely for the stream to fail because these types can accept more than just numbers.

One drawback to formatted input is that it leaves the new line character in the input buffer. This is not really a problem when the next input is a formatted input, but does cause a problem when the next input uses "std::getline()" which reads the new line and moves on not allowing any input from the user.

Just to mention it unformatted input is when you use "std::getline".

For the while loop;

You are right the exclamation point (!) does mean not in this use. By saying "!std::cin" it is checking the value of the good bit. So if the good bit is (1) the(!) changes the result to (0) and the loop conditions fails. Or the opposite.

The std::cin.clear(); is a function to reset the state so the stream can be used again. Normally I put a comment after this, but I missed it here.

The line cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');. Takes two parameters the first is just a large number and the second is a delimiting character to look for. Usually this is the new line (\n), but could be something else. You can look at http://www.cplusplus.com/reference/istream/istream/ignore/ for more information.

The big ominous looking first parameter uses the header files used to compile the program to get the largest size of the input buffer. The biggest reason for using this is because it is more portable between computers and compilers as different compilers may set different values for this along with different values for many other variables or type names.

You could just as easily write std::cin.ignore(1000, '\n'); or std::cin.ignore(1000); and use the default of "\n" for the second parameter.You could also use std::cin.ignore(); which will use the default values of (1) and ('\n'). Sometimes the last example works when you need to just remove the "\n" from the input buffer as long as you are sure that the new line is all that is there.

When you look at the full code:
1
2
3
4
5
6
7
8
9
10
while (!std::cin)
{
	std::cerr << "\n    Invalid entry! Numbers only.\n";

	std::cin.clear();  // <--- Resets the state bits.
	std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');  // <--- Requires header file <limits>.

	std::cout << "\n Please enter operation to perform. Format: a+b or a-b or a*b or a/b: ";
	std::cin >> num1 >> oper >> num2;
}

The part I have not mentioned is line 3. It is a good idea to let the user know that something went wrong. Followed by the last two line to re-prompt and reenter the information.

I have recently learned that "cout" and "cerr" both print to the screen, but they are two different streams. So if something would ever cause "cout" to fail "cerr" will still print to the screen, so "cerr" becomes a better way to print an error message IMO. I am trying to learn to use "cerr" more often rather than blindly counting on "cout" to always work. Truthfully I do not know if this is a better way or just something I think is a good idea. I like to lean towards it being a better way.

Another point: when I or someone else gives you code that starts"std::" you do not have to strip this just because yo are using the line using namespace std; This does not have any effect on the way the code is compiled. Also you can look at this as a learning tool.

When I use the line of code: std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // <--- Requires header file <limits>. I do not write this every time. I make use of VS's "Toolbox" feature. If this is not already on your screen somewhere go to the menu "View" find "Toolbox", maybe half way down or so, and click on it. There you can bits of code that you do not want to type every time that you need it.

Since you are learning I have found this site to be very useful https://www.learncpp.com/ Much bigger than when I first used it and they seem to keep it updated well. Whereas the tutorial here is good, but needs updated as I have read this from several people.

Hope this helps, if not let me know,

Andy
@Andy, a couple of points:

These are the "bad", "fail", "eof" and "good" bits.

Technically there is no "good" bit. If eofbit, failbit, and badbit are 0, then good() returns true.

I have found that using formatted input with a numeric variable has the most advantage.

If your input is line-oriented it's often best to combine std::getline and std::istringstream:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
while (std::getline(fin, line))
{
    std::istringstream iss(line);

    int x{}, y{};
    if (!(iss >> x >> y))
    {
        std::cerr << "Error: line must contain two integers.\n";
        std::exit(EXIT_FAILURE); // or whatever
    }

    // And if you want to check for extra garbage at the end of the line:
    char ch;
    if (iss >> ch) // reads first non-whitespace char, if there is one
    {
        std::cerr << "Error: non-whitespace characters after integers.\n";
        std::exit(EXIT_FAILURE); // or whatever
    }
}

or std::cin.ignore(1000); and use the default of "\n" for the second parameter.

The default isn't '\n'. It's Traits::eof().

I have recently learned that "cout" and "cerr" both print to the screen, but they are two different streams.

The main point of having them be different streams is that they can be separately redirected at the command line. For instance if you ran your program like myprogram > outputfile then cout output will go to the file but cerr output will still go to the terminal.

EDIT:
I forgot to mention that output to cerr is immediately flushed and that it is tied to cout so that cout will be flushed first.

Additionally there is clog, which also writes to stderr but is not automatically flushed nor tied to cout.
Last edited on
Dutch - Thanks for the response and the clarification. As you can tell, I am a real beginner.

Steve
Andy - I am still trying to assimilate all the information in your long note. It appears I need to implement some changes in the other files in order to get the program to run - the header file and other two function cpp files. Still working on it. I am studying and understanding rather than just copy and paste.

Thanks for the help.

Steve
Topic archived. No new replies allowed.