# Lecture 10: Error Handling

\( \newcommand\bigO{\mathrm{O}} \)

In this lecture, we will cover three mechanisms for handling errors:

- Stopping the program (so, not handling the error much)
- Letting the caller know through:
- Exceptions: by specially-handled values that anyone in the chain has to check.
- Optionals: by returning a special wrapper that keeps track of whether the returned value makes sense or not.

## 1 Stopping the program

Suppose, we are implementing our own math library, and implemented the square root function like this (the implementation does not matter much to us):

```
double Sqrt(double a) {
// 20 iterations are enough for doubles
double x = 1;
for (int i = 0; i < 20; ++i) {
// x_{i+1} = x_i - f(x_i)/f'(x_i)
//
// where f(x) = x^2 - a, so the roots are +/- sqrt(a)
// f'(x) = 2x
x = (x + (a / x) ) / 2;
}
return x;
}
```

Then, we can evaluate, say, `Sqrt(4)`

and get `2`

. That looks
sensible. However, the square root function is not defined for negative
numbers. So, we need to report an error. The simplest option we have is to
print an error and exit the program:

```
double Sqrt(double a) {
if (a < 0) {
std::cout << "tried to compute square root of negative number " << a << "\n";
std::exit(1);
}
// 20 iterations are enough for doubles
double x = 1;
for (int i = 0; i < 20; ++i) {
// x_{i+1} = x_i - f(x_i)/f'(x_i)
//
// where f(x) = x^2 - a, so the roots are +/- sqrt(a)
// f'(x) = 2x
x = (x + (a / x) ) / 2;
}
return x;
}
```

So far, everything is nice. Our square root program always returns a correct
value (or it does not return and stops the program). However, the programmer
now needs to be careful when calling `Sqrt`

: they also need to make the check
the input every single time, or the program may randomly crash. Can we do
better? More specifically, can we help the programmer by giving them a
mechanism to *handle the error* of calling `Sqrt`

with an invalid input. The
answer is yes. Enter exceptions.

## 2 Exceptions

C++ has a construct called "exceptions" to handle, well, exceptional
cases. You can read more about them at cppreference.com or in PS 16. Normally,
a function runs and returns a value. However, when an exceptional case
happens, the function may instead **throw** an exception, which means that the
function stops executing and instead of returning a value, it passes an
exception value to the caller (or whichever context is set up to catch that
exception). For example, say we are trying to read a positive number from the
input:

int main() { int value; try { cout << "Enter a positive number: "; cin >> value; if (value < 0) throw value; cout << "The number entered is: " << value << endl; } catch (int e) { cout << "The number entered is not positive:" << e << endl; } cout << "Statement after try/catch block" << endl; return 0; }

Here, everything executes within the try block if execution is normal. Every try block must be accompanied by one or more catch blocks.

- Only when
`value < 0`

would we throw something in the example above. - Statements after throwing an exception within the try block is ignored, and code within the catch block of the exception type is executed. You can think of it as the execution "jumping" to the catch block.
- Regardless if an exception was thrown or not, execution resumes after the try/catch block.

Note that the value we caught in the `catch`

statement is same as the value
thrown by the `throw`

statement. We can now change our square root function
to throw an exception, so the caller can catch it:

```
double Sqrt(double a) {
if (a < 0) {
throw "passed negative value to sqrt";
}
// 20 iterations are enough for doubles
double x = 1;
for (int i = 0; i < 20; ++i) {
// x_{i+1} = x_i - f(x_i)/f'(x_i)
//
// where f(x) = x^2 - a, so the roots are +/- sqrt(a)
// f'(x) = 2x
x = (x + (a / x) ) / 2;
}
return x;
}
```

Now, the caller can catch the exception if they are not sure if the input is
non-negative. They can also call `Sqrt`

a bunch of times, and catch the first
exception that would occur, like so:

```
try {
Sqrt(2);
Sqrt(-4);
Sqrt(3);
Sqrt(0);
Sqrt(-3);
} catch (const char * message) {
cout << "caught error message: " << message << "\n";
}
```

Here, we are catching a `const char *`

because `Sqrt`

is throwing a string
literal, which is of that type. This is inconvenient! Because, we cannot tell
between other things that would throw an exception versus the things `Sqrt`

throws. To solve this, we can define special classes so we can throw objects
of those types:

```
struct SqrtError {};
// later, in Sqrt
double Sqrt(double a) {
if (a < 0) {
throw SqrtError{};
}
// ...
}
// now, we can catch only the errors from Sqrt (assuming nothing else throws
// SqrtError):
try {
Sqrt(x);
} catch (SqrtError& err) {
cout << "passed negative value to sqrt, carrying on\n";
}
```

Here, we are catching the exception by reference because exceptions are often in a class hierarchy with virtual methods.

### 2.1 An exception hierarchy

Let's extend our math library by adding a method to compute quadratic equations. We will be using the classical way of solving \( a x^2 + b x + c = 0 \) by finding the roots \( \tfrac{-b \pm \sqrt{b^2 - 4ac}}{2a} \). There are two things that can go wrong here:

- There are no roots ( \(b^2 -4ac < 0 \) )
- The equation is not truly quadratic ( \( a = 0 \) )

Let's add exception objects for each of these cases, so we can handle them:

```
struct QuadraticError {
virtual string what() {
return "generic quadratic eqn error";
}
};
struct NonQuadraticEqn : public QuadraticError {
string what() override {
return "non-quadratic equation";
}
};
struct NoRoots : public QuadraticError {
string what() override {
return "equation has no roots";
}
};
// Find the roots of given quadratic equation
pair<double, double> SolveQuadraticEqn(double a, double b, double c) {
if (a == 0) {
throw NonQuadraticEqn{};
}
// square root of discriminant
try {
double d = Sqrt(b * b - 4 * a * c);
return { (-b + d) / (2 * a), (-b - d) / (2 * a) };
} catch (SqrtError&) {
throw NoRoots{};
}
}
```

We now have a hierarchy of errors: the user can catch the two specific cases,
or generally any error with quadratic equation solving. For example, this
wrapper function prints the solution or an error message if the equation
cannot be solved by `SolveQuadraticEqn`

:

```
void SolveAndPrint(double a, double b, double c) {
try {
auto [ root1, root2 ] = SolveQuadraticEqn(a, b, c);
cout << "roots of " << a << "*x² + " << b << "*x + " << c << " = 0 are "
<< root1 << " and " << root2 << "\n";
} catch (NonQuadraticEqn&) {
if (b != 0) {
auto root = - c / b;
cout << "the root of " << a << "*x² + " << b << "*x + " << c << " = 0 is "
<< root << "\n";
} else {
cout << "eqn doesn't have roots\n";
}
} catch (NoRoots&) {
cout << "could not solve the equation "
<< a << "*x² + " << b << "*x + " << c << " = 0\n";
}
cout << "solver done\n";
}
```

As a side note: the notation `auto [ root1, root2 ] = SolveQuadraticEqn(a, b, c);`

tells the compiler to break down the result of `SolveQuadraticEqn`

(which is
a struct, specifically a pair), and assign `root1`

to the first field and
`root2`

to the second field.

And, here is a small program that catches the general `QuadraticError`

and
uses the `what()`

virtual method to print the specific error message, all
thanks to dynamic dispatch:

```
int main(int argc, char * argv[]) {
if (argc == 4) {
double a = stod(argv[1]);
double b = stod(argv[2]);
double c = stod(argv[3]);
try {
auto [ root1, root2 ] = SolveQuadraticEqn(a, b, c);
cout << "x1 = " << root1 << ", x2 = " << root2 << "\n";
} catch (QuadraticError &err) {
cout << "could not solve the eqn: " << err.what() << "\n";
}
}
}
```

This setup is what the standard library has: It has a hierarchy of exceptions
inheriting from std::exception, and each exception implements the `what()`

virtual function to print the appropriate message. The exceptions represent
cases like a general `logic_error`

, or more specific `out_of_range`

error,
or `invalid_argument`

.

Before we wrap up, what happens if there is no catcher? For example, try running the following program with the arguments 1 0 4 (that is \( x^2 + 4 = 0 \) ):

```
int main(int argc, char * argv[]) {
if (argc == 4) {
double a = stod(argv[1]);
double b = stod(argv[2]);
double c = stod(argv[3]);
auto [ root1, root2 ] = SolveQuadraticEqn(a, b, c);
cout << "x1 = " << root1 << ", x2 = " << root2 << "\n";
}
}
```

The program would crash with an uncaught exception error. So, exceptions can get tricky to use because they can still crash the program if not handled correctly.

## 3 Optionals

An alternative to exceptions is returning the value in a wrapper that says
"maybe there is a value". For example, we could define a class like this and
return `SqrtResult`

from `Sqrt`

:

```
struct SqrtResult {
bool is_valid;
// valid only if is_valid is true
double value;
};
```

Then, `Sqrt`

can return a valid value only when the input is not negative:

```
SqrtResult Sqrt(double a) noexcept {
if (a < 0) {
// return an invalid value
return SqrtResult(false, 0);
}
// 20 iterations are enough for doubles
double x = 1;
for (int i = 0; i < 20; ++i) {
// x_{i+1} = x_i - f(x_i)/f'(x_i)
//
// where f(x) = x^2 - a, so the roots are +/- sqrt(a)
// f'(x) = 2x
x = (x + (a / x) ) / 2;
}
return SqrtResult(true, x);
}
```

Callers of `Sqrt`

would need to check the `is_valid`

field before using the
value now. Defining a result type for everything would be tedious, we can make
`SqrtResult`

generic to hold a value to get around that. Luckily, the standard
library comes with a type like that: `std::optional`

. `std::optional`

works
like this:

```
template<class T>
class optional {
bool is_valid;
T value;
public:
T& operator * () { return value; }
bool operator bool () { return is_valid; }
// other stuff ...
};
```

So, if we have an optional value, we can check if it is valid like checking if a pointer is valid:

```
int * p = nullptr;
if (p) { // or p != nullptr
// p evaluates to false, so we don't enter here
}
p = new int(5);
if (p) {
// p evaluates to true, so we enter here
}
std::optional<int> o = std::nullopt;
if (o) { // or o != nullopt
// o evaluates to false, so we don't enter here
}
o = 5; // note that we don't need new
if (o) {
// o evaluates to true, so we enter here
}
```

We can also access the value inside, like a pointer:

```
optional<int> o = 42;
cout << *o << endl; // prints "42"
```

However, unlike a pointer, optionals carry the associated data with them (rather than pointing to them), so they destroy the value when they go out of scope.

Now, let's rewrite `Sqrt`

using `optional`

and use it in `SolveQuadraticEqn`

:

```
// Compute square root using Newton's method
optional<double> Sqrt(double a) noexcept {
if (a < 0) {
return nullopt;
}
// 20 iterations are enough for doubles
double x = 1;
for (int i = 0; i < 20; ++i) {
// x_{i+1} = x_i - f(x_i)/f'(x_i)
//
// where f(x) = x^2 - a, so the roots are +/- sqrt(a)
// f'(x) = 2x
x = (x + (a / x) ) / 2;
}
return x;
}
// auto x = Sqrt(5); // x = double OR x = an error value
// Find the roots of given quadratic equation
pair<double, double> SolveQuadraticEqn(double a, double b, double c) {
if (a == 0) {
throw NonQuadraticEqn{};
}
// square root of discriminant
if (optional<double> r = Sqrt(b * b - 4 * a * c)) {
double d = r.value(); // we can also use *r
return { (-b + d) / (2 * a), (-b - d) / (2 * a) };
} else {
throw NoRoots{};
}
}
```

I used `r.value()`

above, which throws an exception if `r`

is empty (that is,
it is equal to `nullopt`

. We could've used `*r`

instead in this case. If `r`

is empty, then `*r`

is undefined behavior.

Also, if you are not familiar with the way I wrote the second conditional
there, `if (optional<double> r = Sqrt(b * b - 4 * a * c))`

is roughly same as:

```
optional<double> r = Sqrt(b * b - 4 * a * c);
if (r)
```

So, the guard of `if`

uses the assigned value `r`

. The benefit of that if
statement is that `r`

is not available in the `else`

block because it is
restricted to only the true branch. This allows us to use the compiler to get
an error if we mistakenly use `r`

in the false branch.