Contributor's Corner

A collection of hopefully helpful information
Functionality Clarity Elegance
mylogo

Dedication: An early version of the material below was posted a couple of years back on devshed forums. I was the original author of that material. This post is therefore dedicated to Thaminda, the site admin who saw fit to repost the material under his (or her) own name without any attribution or acknowledgement and (worse) making it completely pointless for the author to correct any errors in the original post. — Grumpy —

  C++ Exceptions   

"He fights his fate, often desperately...forever entering bold exceptions to the rulings of the bench of gods."
—H. L. Mencken
The Basics of Exceptions

Before getting into advanced concepts related to using exceptions, I'll describe the basics. Most of the following may be gleaned from basic texts, although I've worded things slightly differently.

Exceptions, as an error reporting and recovering mechanism, are a feature of C++ (not C). As described in many texts, the basic usage comes down to

try 
{ 
     if (some_condition) 
     { 
         ExceptionType x; 
         throw x; 
     } 
} 
catch (ExceptionType x) 
{ 
         // recover from error 
} 
Features of Exceptions

Core features of exceptions include:

  1. They must be caught, or the program will exit (via a call to terminate() which, by default, calls abort()).
  2. The throwing of exceptions need not be in the same function as the try/catch block.
  3. When an exception is thrown, control is passed to the first matching catch() clause in the call stack for recovery. Any objects local to intervening functions are also destructed.
  4. Exceptions may be caught via value or reference. If caught by reference, they may be used polyporphically.
  5. The exception types that may be thrown by a function may be specified. If an exception is thrown within the function that does not match the "throw specification", the program will exit via a call to abort().
  6. Within a catch handler, a new exception may be thrown or the caught exception may be rethrown.
These features are exhibited in the following sample;
#include <string> 

class X { public: int Type() const {return 1;}; }; class Y : public X { public: int Type() const {return 2;}; };

class Other {}; void FunctionThatThrows() throw (X) { Y y; throw y; // OK; Y is derived from X }

void AnotherFunctionThatThrows() throw (X) { Other a; throw a; // will result in call to abort(); }

void IntermediateFunction(bool call) { std::string hello("Hello");

// Just to illustrate how to do it, we catch all exceptions and rethrow them. // Such a construct gives the same effect that the compiler would give us // anyway try { if (call) FunctionThatThrows(); // the string hello will be cleaned up (i.e. destructed) under all circumstances. // if an exception is not thrown (eg call is false) it will be cleaned up. // if an exception is thrown (eg call is true) it will still be cleaned up. } catch (...) { throw; // rethrow exception } }

int main() { try { std::string x("x"); IntermediateFunction(false); std::string y("y"); // both x and y will be cleaned up } // no exception thrown, so neither of the catch clauses will be invoked catch (X &x) { std::cout << x.Type() << std::endl; } catch (...) { }

try { std::string x("x"); IntermediateFunction(true); // this will throw std::string y("y"); // so this line is never executed // but x will still be cleaned up (i.e. it's destructor will be invoked). } // an exception of type Y (derived from X) will be thrown, so the first // catch clause will have effect catch (X &x) { std::cout << x.Type() << std::endl; // exception of type Y thrown, so value of 2

printed } catch (...) // catch clauses checked in order they are declared. // First clause matches, so this one has no effect { }

return 0; }

The above is what one may normally work out by reading most basic texts. The problem is, while the material is accurate, it does not represent how one may effectively use exceptions. In particular, most people attempting to manage exceptions in their code sprinkle try/catch clauses everywhere. This actually makes their code much more complicated than it needs to be and, because try/catch blocks must wrap other code, makes code much more difficult to understand and maintain. Ironically, it turns out that a basic rule of thumb to using exceptions effectively is that use of try/catch should be avoided wherever possible. This allows most error handling code to be localised and reduces phenomena of having to reuse exception handling code. The purpose of the remainder of this article is to explain concepts and techniques that help reduce the need to write lots of exception handling code.

Concepts & Techniques: Rule One