Wednesday, December 15, 2010

Factoring exception handling code in C++

When writing exception handlers (e.g. around calls to database or network related code), you often end up with the same set of catch clauses at each call site. Suppose you end up with the following code:

try {
  //...
} catch (std::runtime_error& e) {
  std::cout << "Runtime error: " << e.what() << std::endl;
} catch (...) {
  std::cout << "Unknown exception!" << std::endl;
}

You have this piece of code repeated dozens of times, which is not really a Good Thing(tm)... A little C++ trick can help you avoid those repetitions: (1) factor the common code into a single function or method that rethrows the active exception and catches it immediately, and (2) catch all exceptions at the call sites and call this handler!

#include <iostream>
#include <stdexcept>

void exception_handler() {
  try {
    throw; //rethrow the current exception
  } catch (std::runtime_error& e) {
    std::cout << "Runtime error: " << e.what() << std::endl;
  } catch (...) {
    std::cout << "Unknown exception!" << std::endl;
  }
}

int main() {
  try {
    throw std::runtime_error("unable to comply...");
  } catch (...) { exception_handler(); }

  try {
    throw 1;
  } catch (...) { exception_handler(); }

  return 0;
}

You must be careful to not call exception_handler() outside a catch clause: the trow statement calls terminate() if there is no active exception to rethrow!

You may wonder if one could use the std::uncaught_exception() function to protect against this.

void exception_handler() {
  try {
    if (!std::uncaught_exception()) {
      return; // Nothing to rethrow...
    }
    throw; //rethrow the current exception
  } ...
}

But this does not work: uncaught_exception() "returns true after completing evaluation of the object to be thrown until completing the initialization of the exception-declaration in the matching handler", as the standard says. And when exception_handler is entered, this is generally from the catch clause, and the exception has thus been caught... Your handler would now simply swallow all the exceptions!...

Actually, uncaught_exception is pretty much useless...

1 comment: