Saturday, October 24, 2009

Episode Eleven: Exceptions for the Exceptional

An exception breaks the normal flow of code under given conditions, and forces the caller to deal with them, as opposed to return codes. In C++, exceptions should be used for the exceptional situations only. An exceptional situation is something that should never happen (yet it might). This is not the situation with other languages; notably Python throws an exception to signal the end of an iteration.



Exceptions are objects, and as such, the implementation needs space to create them in order to throw them. The standard dictates that if an out of memory condition is reached while creating an exception to be thrown, an std::bad_alloc exception is thrown instead. A C++ implementation is required to have enough spare memory to be able to throw bad_alloc in case of memory exhaustion. This means that the only exception that is guaranteed to be thrown is std::bad_alloc, any other exception could be lost in the face of memory exhaustion.

When throwing an exception results in another exception being thrown --from the copy-constructor/destructor of the exception being thrown, or from the destructors called during stack unwinding--, std::terminate() is invoked which aborts execution. That's why throwing destructors are discouraged, as well as exception classes copy-constructors that allocate memory, among others.

When an exception is thrown and there is no matching handler std::terminate() is also invoked, however in this situation is implementation-defined whether stack unwinding occurs, which could lead to leaked resources.

When std::unexpected() is invoked and no user defined unexpected handler is set std::terminate() is invoked. std::unexpected() is called by the implementation when a function exits via an exception not allowed by its exception-specification. An exception-specification tends to have consequences that require very careful thought to understand. Particularly, exception-specifications are enforced at *run-time* (for those implementations that do honor them) instead of at compile-time (as in Java).

What does all this means? If we were to end iteration the Python way, a simple for loop could result in program abortion and leaked resources. Clearly not what is intended for such a situation that's not only possible, but expected.

No comments:

Post a Comment