Error Handling – The C++ Way

In my experience as software developer, I noticed that there are lots of people that write in C++, but do not use exceptions. They prefer to use return codes instead. I guess that this is caused by the late addition of exceptions in the C++ standard. Whatever the reason is, personally, I prefer to use exceptions as an error handling technique.

Almost 10 years ago I started programing in Object Pascal (Borland Delphi). It was really popular in Bulgaria at that time. I used to like it and I still think that Pascal is the best programming language for education purposes. In my opinion VCL is well designed framework, that is teaching developers good programming practices. Anyway, exceptions are widely used in VCL. So I got used to exceptions and when I moved to C++, I continued using this error handling mechanism.

So why use exceptions?

Overall, exceptions can improve software quality, but the most important reason in favor of their use is that unlike return codes exceptions can not be ignored. If an exception is thrown someone will have to catch it or the program will be terminated. There is no mercy :-).

Apart from that, exceptions eliminate one of the reasons for if statements. As we all know if statements are evil (grrr I hate nested ifs). When used for error handling if statements are adding unnecessary control flow complexity. Writing unit tests, reading and testing the code becomes difficult.

Another advantage of exceptions is that the error handling is consistent across different modules.

Syntax (or going back to school)

The exception mechanisms consists of try blocks, catch handlers, throw expressions and exception specifications:

Code
ClassA::method(void* p) throw1(Exception)
{
   try2
   {
      if (null == p)
         throw Exception3;
   }
   catch4(const Exception&) {
      //Some code
      throw5;
   }
}

1. Exception specification – a list of exceptions that a function can throw. It’s not enforced by the complier. Empty specifications states that the function doesn’t throw exceptions. If an exception is thrown that is not in the list, the application will be terminated. That’s why it’s recommended to use empty specifications only. An exception different from the one(s) in the specification might come from a nested function call for example.

2. Try block – area of the code that detects exceptions.

3. Throw expression – raises exception. C++ allows you to throw virtually anything, but it’s best to throw objects. I prefer to derive my exceptions classes from the base exception class of the framework I’m using. For example when  developing MFC applications I use CException, when I use STL my exceptions are derived from the std::exception class, etc.

4. Catch handler – has signature denoting an exception type.

It’s best to catch by reference, this way avoiding the overhead from copy constructors. There are some exceptions though. For example in MFC the exceptions are thrown in the heap, not in the stack, so you’ve got to catch by pointer. In MFC don’t forget to invoke the Delete() method after you’re done with the exception.

You can catch a specific exception (like in the sample above) or you can catch all exceptions:

catch(…)

Don’t forget to order the catch handlers from the more specific to the more generic ones. The catch all handler should be last.

5. Rethrow – useful when some preliminary processing is needed.

Usage specifics

Exceptions in constructors

Constructors don’t have a return type, so the only way to indicate an error in a constructor is by throwing an exception. If a constructor throws an exception, the object’s destructor is not run. Destructors are called for every automatic object constructed in a try block before an exception is thrown. So keep in mind that you’ll have to manually free the heap objects and other resources allocated in the constructor.

Overloaded operators

Another case where exceptions are the only solution are overloaded operators.

Exceptions in destructors

Throwing an exception from the destructor will cause the termination of the application. So never throw exceptions from destructors.

Exceptions and multithreading

Exception-handling is safe for multithreading. Exceptions in one thread do not interfere with exceptions in other threads. Exceptions can not be used to communicate across threads. An exception thrown from one thread cannot be caught in another.

Exception safety

An operation on an object is said to be exception safe if that operation leaves the object in a valid state when the operation is terminated by throwing an exception. This means that:

  • resources should not be leaked;
  • the object should remain in a well-defined state so that execution can continue;
  • when an error is encountered, it is reported to the caller.

David Abrahams defines three exception safety guarantees. Exception safety guarantee is basically a contract between a component and its clients:

  1. The basic guarantee: that failed operations may alter program state, but no leaks occur and affected objects/modules are still destructible and usable .
  2. The strong guarantee: that the operation has either completed successfully or thrown an exception, leaving the program state exactly as it was before the operation started.
  3. The no-throw guarantee: that the operation will not throw an exception.

Final thoughts

Don’t define too many exceptions classes. A new class should be created only if it needs to be handled differently.

Don’t leak implementation details by allowing low-level exceptions to find their way to higher layers. Lower-level exceptions need to be recasted to higher-level ones. A consequence from this advice is that you should not  repeatedly rethrow the same exceptions.

Exceptions should be used only to handle errors. Don’t use exceptions to return values.

The opponents of exceptions say that exception handling is adding overhead. The overhead comes from both size and execution times. While this is undoubtedly true, the benefits from their use overcomes this disadvantage. Exceptions lead to cleaner, safer, easier to maintain code.

Finally, I don’t think that there is “one size fits all” solution, so do what’s best for your case, but remember that exceptions are the C++ way of handling errors 🙂

You can leave a response, or trackback from your own site.

One response to “Error Handling – The C++ Way”

  1. Mariusz Wojtysiak says:

    Nice article.

    Personally I also use exceptions, but avoid declaring exceptions in method declaration, like here:
    ClassA::method(void* p) // throw(Exception) – declaration as comment!

    Why? Please see below code:

    struct MyException{ MyException( const string &what ){} };
    struct OtherException{ OtherException( const string &what ){} };

    void initCalculator() throw( OtherException )
    {
    throw OtherException( “calculator not installed” );
    }

    int calculate( int something ) throw( MyException )
    {
    int calculated = 0;

    if( something 0″ );
    }

    initCalculator();

    return calculated;
    }

    int main()
    {
    try
    {
    calculate( 2 );
    cout << "ok" << endl;
    }
    catch( const MyException &exc )
    {
    cout << "MyException" << endl;
    }
    catch( const OtherException &exc )
    {
    cout << "OtherException" << endl;
    }
    catch( … )
    {
    cout << "different exception" << endl;
    }
    return 0;
    }

    We expect that OtherException( "calculator not installed" ) will be thrown.
    And will be catched by catch( const OtherException &exc ).

    … but at least under gcc above code produces following runtime error:

    This application has requested the Runtime to terminate it in an unusual way.
    Please contact the application's support team for more information.

    Above error is like for program with unhandled exception. But we have there catch(…)!!!

    What happens?
    Compiler see declaration:

    int calculate( int something ) throw( MyException )

    and assume that only MyException (and not others) may be thrown.

    So when calculate() throws other exception, then terminate() is called immediatelly.

    So it's safer NOT to declare exceptions in formal way. We can declare exception only in comment.

    When you don't declare exception in method declaration, then every kind of exception may be thrown, so catch(…) WILL catch every exception.

Leave a Reply