There is no need for complicated statements.
You just need to understand that IO operation notice, when there was a problem. And then they set failure bits, which you can check.
Please look in the CPP reference at this link. There is a long description of what could happen and what failure bit will be set and how it can be tested.
So, if you use for example the stand extraction operator >>
like in std::cin >> radius
then this operator will try to read a value from the console and convert it to a double. If it cannot do that, because you entered for example "abc" instead of a number, a failure bit will be set. The std::cin
is then in state fail and does not work any longer.
If you want to continue to use std::cin
then you must clear the fail bits with the clear()
function. So, you need to write std::cin.clear();
.
But this is not sufficient. There might be still some other characters in the input buffer. And those need to be removed. Imagine that you enter "XYZ" instead of a number, then std::cin
will go into failure state after reading the first character 'X'. We need to eliminate all the wrong characters, because otherwise, they will be read again with the next >>
operation.
For this we have the function ignore
. Please read here about the function and look at the example at the bottom of the page. Exactly what you need.
Next: How can we check for an error of an IO operation?
You may have heard that we can chain IO operations. For example: int a,b; std::cin >> a >> b;
or, for the output case std::cout << value << "\n";
Why does this work? You need to understand the the extraction and inserter operator >>
and <<
return a reference to the stream for which they have been called.
So, std::cin >> a;
will return a reference to std::cin
. And that is the reason why you can chain IO operations.
std::cin >> a >> b;
will first do std::cin >> a
which will return std::cin
. The rest of the expression will now be std::cin >> b;
. also this will be performed and again std::cin
will be returned.
And this we can use. The basic:ios
has 2 operators to check the failure state.
- the bool operator
- the not operator
!
So, you can check the state of std::cin
simply with if (std::cin)
. And because the if
-statement expects a bool expression, it will call the streams bool
operator. And with that get the state.
And now, what we learned above: if (std::cin >> a)
will try to read a value into "a" then return std::cin
and then its bool
operator is called.
Now we found a possibility to check for a correct IO operation with if (std::cin >> radius)
But of course we can do more test in the if statement. You have often seen and
&& or or
|| operators in conditions. You can make use of it. And especially you can make use of boolean shortcut evaluation.
Meaning, if the outcome of a condition is already clear by evaluation the first term, then the second term will not be evaluated.
So, we can write if ((std::cin >> radius) and (radius > 0.0))
to check for a valid input. If the reading of the input fails, then the check for greater than 0 will not be executed. It will only be executed, if the input was successful.
With all the above, we can now draft the below very simple solution:
#include <iostream>
#include <limits>
int main() {
double radius = 0.0;
bool valueOK = false;
while (not valueOK) {
std::cout << "\n\nInsert radius. A positive value: ";
if ((std::cin >> radius) and (radius > 0.0))
valueOK = true;
else {
std::cout << "\n\n***Error: invalid input\n";
}
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
const double PI = 3.14159265358979323846;
std::cout << "\n\nSphere Radius:\t" << radius
<< "\nSphere area:\t" << 4.0 * PI * radius * radius
<< "\nSphere volume:\t" << 4.0 / 3.0 * PI * radius * radius * radius << '\n';
}
No need for complicated statements.