Exceptions - Part 1

(Nine days to better error handling)

Error Handling

Recall that given a quadratic equation of the form:
\(ax^2 + bx + c = 0\)

We can solve the equation for its roots with this formula:

$$x = {-b \pm \sqrt{b^2-4ac} \over 2a}.$$

Day One

For simplicity, we will only calculate one root (the "+" portion of the equation).

The QRoot function (for Quadratic Root) looks like:

double QRoot(double a, double b, double c)
{
  double determinant = (b * b) - (4 * a * c);
  return (-b + std::sqrt(determinant)) / (2 * a);
}
We would use it something like this:
#include <iostream>
#include <cmath>

double QRoot(double a, double b, double c)
{
  double determinant = (b * b) - (4 * a * c);
  return (-b + std::sqrt(determinant)) / (2 * a);
}

int main()
{
    // -0.438447
  std::cout << "QRoot a=1, b=5, c=2: " << QRoot(1, 5, 2) << std::endl;

    // Error, taking square root of negative number (-nan)
  std::cout << "QRoot a=1, b=2, c=5: " << QRoot(1, 2, 5) << std::endl;

    // Error, divide by 0 (-nan)
  std::cout << "QRoot a=0, b=2, c=5: " << QRoot(0, 2, 5) << std::endl;
  
  return 0;
}
The output
QRoot a=1, b=5, c=2: -0.438447
QRoot a=1, b=2, c=5: -nan
QRoot a=0, b=2, c=5: -nan
What are the pros and cons of this approach?

Day Two

double QRoot(double a, double b, double c)
{
  double determinant = (b * b) - (4 * a * c);

  if (determinant < 0) // protected against: std::sqrt(-x)
  {
    std::cout << "Can't take square root of a negative number." << std::endl;
    std::abort();
  }
  else if (a == 0)     // protected against: x / 0
  {
    std::cout << "Division by 0." << std::endl;
    std::abort();
  }

  return (-b + std::sqrt(determinant)) / (2 * a);
}
Note: The client code is unchanged. Output from the abort() function (depends on the version of the compiler.)

GNU (on Cygwin):

Can't take square root of a negative number.
    160 [sig] a 1716 open_stackdumpfile: Dumping stack trace to a.exe.stackdump
    160 [sig] a 1716 open_stackdumpfile: Dumping stack trace to a.exe.stackdump
1572257 [sig] a 1716 E:\Data\Courses\Notes\CS170\Code\Exceptions\a.exe: *** fatal error - 
E:\Data\Courses\Notes\CS170\Code\Exceptions\a.exe: *** called with threadlist_ix -1
Microsoft:
Can't take square root of a negative number.

This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information. 
Borland:
Can't take square root of a negative number.

Abnormal program termination
What are the pros and cons of this approach?

Day Three

bool QRoot(double a, double b, double c, double *result) // Could use a reference as well
{
  double determinant = (b * b) - (4 * a * c);

    // protected against: std::sqrt(-x), x / 0
  if ( (determinant < 0) || (a == 0) ) 
  {
    *result = 0.0;  // return something safe
    return false; // indicates there was a problem
  }

  *result = (-b + std::sqrt(determinant)) / (2 * a);
  return true;  // indicates all is well
}

int main()
{
  double answer;
  bool success = QRoot(1, 5, 2, &answer);
  if (success)
    std::cout << "QRoot a=1, b=5, c=2: " << answer << std::endl;
  else
    std::cout << "QRoot failed for some reason" << std::endl;
    
  return 0;
}
What are the pros and cons of this approach?

This is the biggest problem. We can't use the function call in an expression:

double result = QRoot(1, 5, 2) + QRoot(5, 2, 1) + QRoot(3, 2, 1);
We have to do something tedious like this:
double result = 0;

if (QRoot(1, 5, 2, &answer))
{
  result += answer;
  if (QRoot(5, 2, 1, &answer))
  {
    result += answer;
    if (QRoot(3, 1, 2, &answer))
      result += answer;
  }
}
Of course, neither of them are actually dealing with the situation where it fails.

Day Four (read all about exceptions)

These examples use simple built-in data types for the exception types. You will likely never do that. This is to keep the examples very simple while focusing on the "exceptional" part of the discussion. Proper use of exception classes will be shown near the end.

A format of the try...catch mechanism:
int main()
{
  . . .
   
  try
  {
    // code that might cause an exception (throw) and needs
    // to be protected
  }
  catch (???)  // which type of exception to catch?
  {
    // code that will handle the exception (catch) from
    // the try block above
  }

  . . .
  
}
You can catch multiple exceptions thrown from a try block:
int main()
{
  . . .
  
  try
  {
    // code that might cause an exception (throw) and needs
    // to be protected
  }
  catch (const char *p) // catch a const char pointer
  {
    // code that will handle the char pointer exception from
    // the try block above
  }
  catch (int i) // catch an integer
  {
    // code that will handle the integer exception from
    // the try block above
  }
  catch (std::exception e) // catch an "exception" object
  {
    // code that will handle the "exception" object from
    // the try block above
  }
  
  . . .
  
}
Notes:

Day Five (Use exceptions)

The client and non-client code has been modified now:
double QRoot(double a, double b, double c)
{
  double determinant = (b * b) - (4 * a * c);

    // protected against std::sqrt(-x) and division by 0
  if (determinant < 0) 
    throw("Can't take square root of a negative number.");
  else if (a == 0)     
    throw("Division by 0.");

    // We only reach this point if no exception was thrown
  return (-b + std::sqrt(determinant)) / (2 * a);
}

int main()
{
  try  // protect code
  {
    std::cout << "QRoot a=0, b=5, c=2: " << QRoot(0, 5, 2) << std::endl;
  }
  catch (const char *message)  // catch a const char pointer exception
  {
    std::cout << message << std::endl;
  }
  
  return 0;
}

Output:
Division by 0.

Day Six

All STL functions indicate which kinds of exceptions, if any, are thrown. Here are some examples:

at Returns a reference to the element at specified location pos, with bounds checking.
insert Inserts elements at the specified location in the container.
reserve Increase the capacity of the vector (the total number of elements that the vector can hold without requiring reallocation).
clear Erases all elements from the container. After this call, size() returns zero.

Note:

(Dynamic) exception specifications have been deprecated in C++11 so you won't use them. They have been replaced with the noexcept specifier, which is covered later. It's still worthwhile to see why they were invented and why they've changed.

// This function promises to only throw a const char * or a double, nothing else
double QRoot(double a, double b, double c) throw(const char *, double)
{
  double determinant = (b * b) - (4 * a * c);

    // protected against std::sqrt(-x) and division by 0
  if (determinant < 0) 
    throw(determinant);       // throw double
  else if (a == 0)     
    throw("Division by 0.");  // throw const char *

    // We only reach this point if no exception was thrown
  return (-b + std::sqrt(determinant)) / (2 * a);
}

int main()
{
  try  // protect code
  {
    std::cout << "QRoot a=3, b=2, c=1: " << QRoot(3, 2, 1) << std::endl;
  }
  catch (const char *message)  // catch a const char pointer exception
  {
    std::cout << message << std::endl;
  }
  catch (double value)  // catch a double exception
  {
    std::cout << value << std::endl;
  }
  
  return 0;
}

Output:
-8
To indicate that a function doesn't throw any exceptions, use empty parentheses:
double SomeFun(double a, double b, double c) throw()
{
  ...
}
What does the following output?
int main()
{
    // protect code
  try 
  { 
      // Determinant will be negative
    std::cout << "QRoot a=3, b=2, c=1: " << QRoot(3, 2, 1) << std::endl;
  }
  catch (const char *message) // catch a const char pointer exception
  {  
    std::cout << message << std::endl;
  }
  catch (double value) // catch a double exception
  {   
    std::cout << value << std::endl;
  }

    // protect code
  try 
  { 
      // a is 0 (divide by 0)
    std::cout << "QRoot a=0, b=2, c=1: " << QRoot(0, 2, 1) << std::endl;
  }
  catch (const char *message) // catch a const char pointer exception
  {  
    std::cout << message << std::endl;
  }
  catch (double value) // catch a double exception
  {   
    std::cout << value << std::endl;
  }
  
  return 0;
}
What does the following output?
int main()
{
    // protect code
  try { 
    std::cout << "QRoot a=3, b=2, c=1: " << QRoot(3, 2, 1) << std::endl;
    std::cout << "QRoot a=0, b=2, c=1: " << QRoot(0, 2, 1) << std::endl;
  }
  catch (const char *message) // catch a const char pointer exception
  {  
    std::cout << message << std::endl;
  }
  catch (double value) // catch a double exception
  {   
    std::cout << value << std::endl;
  }
  
  return 0;
}

Day Six and a half

Previous versions of C++ have had exception specifications. This was a mechanism that allowed the programmer to "tag" functions with the types of exceptions that might be thrown. For example, this lookup function can potentially throw an exception if the index is out of range:
int lookup(const vector<int>& v, int index) throw(std::out_of_range)
{
  return v.at(index); // This method does range checking
}
The code at the end:
throw (std::out_of_range)
is the exception specification and informs the programmer what may be thrown from the function. It also tells the compiler to prevent any other exceptions from being thrown. If any exception, other than std::out_of_range is thrown, the entire program is terminated.

This is sample code that calls the function above:

vector<int> v;
v.push_back(10);
v.push_back(20);
v.push_back(30);
try
{
  cout << lookup(v, 2) << endl; // Prints 30
  cout << lookup(v, 5) << endl; // Throws a std::out_of_range exception
}
catch (const std::out_of_range& ex)
{
  cout << ex.what() << endl; // Display details about the exception
}
Output:
30
vector::_M_range_check: __n (which is 5) >= this->size() (which is 3)

There are 3 varieties of exception specifications:
// foo1 will throw no exceptions
int foo1() throw();

// foo2 will only throw std::out_of_range or std:bad_alloc
int foo2() throw (std::out_of_range, std::bad_alloc);

// foo3 may thrown any exception
int foo3();
  1. foo1 - An empty exception specification may not throw any exception.
  2. foo2 - A non-empty exception specification may only throw the exceptions listed.
  3. foo3 - A missing exception specification may throw any exception.
For various reasons, exception specifications never worked out as intended and have been deprecated for several years. Now, with C++11, there is a replacement. As it turns out, the important information that the compiler wants to know is if the function throws an exception or not. The compiler doesn't care about which exception is thrown. The new technique reflects this change:
// Function will not throw an exception
void foobar1() noexcept(true)
{
  // do something guaranteed to be safe
}

// Function may throw an exception
void foobar2() noexcept(false)
{
  // do something possibly unsafe
}

// foobar3 may throw an exception only if foobar2 may throw an exception
void foobar3() noexcept(noexcept(foobar2()))  
{
  foobar2(); // call possibly unsafe function
}

// foobar4 will not throw an exception (protects unsafe call to foobar2)
void foobar4() noexcept(true)  
{
  try
  {
    foobar2(); // call possibly unsafe function
  }
  catch (/* whatever foobar2 throws */)
  {
    // do something with the exception
  }

  // safely continue excecuting...
}
When declaring a function to be noexcept, these both mean the same thing:
void foobar() noexcept(true) // explicit
void foobar() noexcept       // implicit
On a side note, it's a little unfortunate that the terminology kind of reverses the definition of on/true/enabled with off/false/disabled. By setting noexcept to true, you are disabling the ability to throw exceptions. It has always seemed strange to me when you "turn something off" by answering in the affirmative:
Q: "Do you want me to not turn the lights off?"
A: Yes, please do not turn them off.
Most English speakers will ask in the affirmative:
Q: "Do you want me to turn the lights off?"
A: No, please do not turn them off.
This would mean we would have except(true) to allow exceptions and except(false) to not allow them. Anyhoo...

Notes:

Day Seven

Unwinding the Stack after an Exception

Assuming the same code for the QRoot function, what will this code do

  1. assuming QRoot does not throw an exception?
  2. assuming QRoot throws an exception? (const char *)
void f1()
{
  std::cout << "Starting f1..." << std::endl;
  QRoot(...);  // program flow depends on this call
  std::cout << "Ending f1..." << std::endl;
}

void f2()
{
  std::cout << "Starting f2..." << std::endl;
  f1();
  std::cout << "Ending f2..." << std::endl;
}

int main()
{
    // protect code
  try { 
    // Any code here will always execute...
    f2();
    // Any code here may or may not execute...
  }
  catch (const char *message) // catch a const char pointer exception
  {  
    std::cout << message << std::endl;
  }
  catch (double value) // catch a double exception
  {   
    std::cout << value << std::endl;
  }
  
  return 0;
}
Assuming this call in f1: (no exception is thrown)
QRoot(1, 5, 3);  
We get
Starting f2...
Starting f1...
Ending f1...
Ending f2...

Assuming this call in f1: (division by zero)
QRoot(0, 5, 3);  
We get
Starting f2...
Starting f1...
Division by 0.

Re-throwing Exceptions


void f1()
{
  try 
  {
    QRoot(0, 5, 3);  // division by 0
  }
  catch (const char *s) 
  {
    throw("Error! Please call 1-800-DIV-ZERO for help");
  }
}

int main()
{
    // protect code
  try 
  { 
    f1();
  }
  catch (const char *message) // catch a const char pointer exception
  {  
    std::cout << message << std::endl;
  }
  
  return 0;
}

Output:
Error! Please call 1-800-DIV-ZERO for help
Remember this rule:

NEVER catch an exception that you do not intend to do anything about. If you don't know what to do with the exception that you catch, DO NOT CATCH IT! It is meant for some other part of the program to handle. If you have code that catches an exception and just re-throws the exception just caught, you are guilty of ignoring this rule.

Day Eight (Exception Classes)

class SomeClass
{
  // code here
};

void f1()
{
  throw SomeClass(); // construct and throw SomeClass object
}

int main()
{
  try 
  { 
    f1();
  }
  catch (const SomeClass &s) 
  {  
    std::cout << "Caught an exception of type SomeClass" << std::endl;
  }
  
  return 0;
}

Modifying our String Class

Recall the String class we developed:
class String
{
  private:
    char *string_;     // the "real" string

  public:

      // Constructors, destructor, etc...

      // overloaded [] operators for subscripting
    char & operator[](int index);
    const char & operator[](int index) const;
};
Our original "error handling":
char & String::operator[](int index)
{
  int len = strlen(string_);     // Get length of internal string
  if (index < 0 || index >= len) // Make sure the index is valid
  {
    std::cerr << "Bad index" << std::endl; // If bad, print message
    abort();                          //   terminate program
  }
  else
    return string_[index]; // Return the char at index
}
Adding an Exception Class

  1. Create a class that will be used to "announce" subscript exceptions.
  2. Write code to throw the exception, if necessary. (String class)
  3. Wrap the potentially "unsafe" code in a try block. (Client code)
  4. Include a catch block in the client to handle the exception. (Client code)
class SubscriptError
{
  public:
    SubscriptError(int Subscript) : subscript_(Subscript) {};
    int GetSubscript() const { return subscript_; }

  private:
    int subscript_;
};
Implementation:
char& String::operator[](int index) 
{
  int len = strlen(string_);     // Get length of internal string
  if (index < 0 || index >= len) // Make sure the index is valid
    throw SubscriptError(index); // Throw exception if invalid

  return string_[index];         // Return the char at index
}
We would code the client like this:

int main()
{
  String s("Hello"); // Create string "Hello"
  try 
  {
    std::cout << s[0] << std::endl; // Get the first character and print it
    s[9] = 'C';                     // Attempt to change tenth character
    std::cout << s << std::endl;     
  }
  catch (const SubscriptError &se) 
  {
    std::cout << "Bad subscript: " << se.GetSubscript() << std::endl;
  }
  
  return 0;
}

Output:
H
Bad subscript: 9

Handling Memory Allocation Failure

Now that we know how to deal with out-of-memory errors in C++, we can deal with them.

List::Node *List::new_node(int data) const
{
  Node *node;

  try
  {
    node = new Node(data); // create the node
  }
  catch (const std::bad_alloc& ex)
  {
    std::cout << "New failed" << std::endl;
    std::cout << ex.what() << std::endl;
    std::abort(); // Maybe do something better?
  }

  return node;
}
Modified example from cppreference.com:
#include <iostream> // cout, endl
#include <new>      // set_new_handler
//#include <unistd.h> // sleep

static int allocs = 0;

void handler()
{
  //sleep(2);
  std::cout << "Memory allocation failed, terminating. [" 
            << allocs << "]" << std::endl;

    // Reset new handler (will throw std::bad_alloc)
  std::set_new_handler(0);
}
 
int main()
{
    // Install new handler (no exception will be thrown)
  std::set_new_handler(handler);

  try 
  {
      // Consume all memory
    while (true) 
    {
      std::cout << "Allocating... " << ++allocs << std::endl;
      new double[100000000]; // 800,000,000 bytes
    }
  } 
  catch (const std::bad_alloc& e) 
  {
    std::cout << "Allocation failed: " << e.what() << '\n';
  }

  return 0;
}
Example showing recovery from out-of-memory errors:
#include <iostream> // cout, endl
#include <new>      // set_new_handler
//#include <unistd.h> // sleep

static int allocs = 0;
static double* memory[200000]; // Magic number! Make sure you don't allocate more than this.

static int errors = 1;

void handler()
{
  std::cout << "Memory allocation failed, terminating. [" 
            << allocs << "]" << std::endl;

  std::cout << "Out of memory: " << errors << std::endl;
  //sleep(1);

    // Release all of the memory
  for (int i = 0; i < allocs; i++)
    delete [] memory[i];

  allocs = 0;
  errors++;

    // Reset new handler (will throw std::bad_alloc)
  //std::set_new_handler(0);
}
 
int main()
{
    // Install new handler (no exception will be thrown)
  std::set_new_handler(handler);
  try 
  {
      // Consume all memory
    while (true) 
    {
      std::cout << "Allocating... " << allocs << std::endl;
      memory[allocs++] = new double[100000000]; // 800,000,000 bytes
    }
  } 
  catch (const std::bad_alloc& e) 
  {
    std::cout << "Allocation failed: " << e.what() << '\n';
  }

  return 0;
}
Note: There is actually a potential bug in the code above.

Day Nine (Derived exception Classes)

It is common to derive all exception objects from the exception class:

class exception 
{
  public:
    exception() throw();
    exception(const exception& rhs) throw();
    exception& operator=(const exception& rhs) throw();
    virtual ~exception() throw();
    virtual const char *what() const throw();
};
Deriving SubscriptError from exception:
class SubscriptError : public std::exception
{
  private:
    int subscript_;

  public:
    SubscriptError(int Subscript) : subscript_(Subscript) {};
    int GetSubscript() const { return subscript_; }
    virtual const char *what() const throw();
    {
      static char buff[80];
      sprintf(buff, "Subscript error at index: %d", subscript_);
      return buff;
    }
};
Now, the client should call the what() method instead:

int main()
{
  String s("Hello"); // Create string "Hello"
  try
  {
    std::cout << s[0] << std::endl; // Get the first character and print it
    s[9] = 'C';                     // Attempt to change ninth character
    std::cout << s << std::endl;     
  }
  catch (const SubscriptError &se)
  {
    std::cout << se.what() << std::endl;
  }
  
  return 0;
}

Output:
H
Subscript error at index: 9
Suppose you wanted to be more like the error emitted from STL's vector class and provide the length of the String in the error message:
class SubscriptError : public std::exception
{
  private:
    int subscript_;
    int length_;

  public:
    SubscriptError(int Subscript, int len) : subscript_(Subscript), length(len) {};
    int GetSubscript() const { return subscript_; }
    virtual const char *what() const throw();
    {
      static char buff[80];
      sprintf(buff, "Subscript error at index: %i, length is %i", subscript_, length_);
      return buff;
    }
};
char& String::operator[](int index) 
{
  int len = strlen(string_);           // Get length of internal string
  if (index < 0 || index >= len)       // Make sure the index is valid
    throw SubscriptError(index, len);  // Throw exception if invalid

  return string_[index];               // Return the char at index
}
Output:
H
Subscript error at index: 9, length is 5

Note that the dynamic throw specification, throw(), is deprecated in C++11 in favor of exception specifications, noexcept.

Question: What happens if a constructor throws an exception?