14.8.11

Exception guarantees as a design time contract

Reading through GOTW challenges 8 and 59  on exception safety, Tom Cargill's article on exception safety and Herb Sutter's exception safe stack solution to this, etc, the conclusion one comes to is that  exceptions in C++ necessitate a level of extra caution not only during programming but also during design.

The following challenge on GOTW demonstrates different hidden control flows you can stumble upon because of exceptions and so the need to watch every implicit function call with a degree of caution. For example a simple assignment would call an operator= implicitly which might throw an exception. Also the order in which state variables are changed matters so as to make sure an exception does not render the object in an unstable state.

But the key thing to note is that basic guarantee, strong guarantee, no throw guarantee are levels of safety one has to decide on for an interface at design time. This is because it is a contractual agreement with the client code just like any other functional requirements.Not only that, exception safety can sometimes result in a compromise on performance guarantees. So, based on the situation it is necessary to decide on which way to go.

In a generic component it becomes more challenging because exception guarantee of the classes depends on exception guarantees of types on which this class is templatized on, which is not available at the time class is written.STL, for example provides basic guarantee in many places because a user code can wrap around this to provide stronger guarantees if needed. It also makes a few assumptions like the destructor of the type we templatise on, should not throw, if its assignment operator and copy constructor doesn't throw then a vector::erase will provide a strong guarantee, etc.

I also realised a question I had for a long time. If you wondered why std::stack and std::queue have a pop() function that doesn't return anything but rather have a front() or top() function to be used later. This is to provide exception safety again. If pop() had returned the popped out object and if the operator= of that object threw an exception then we would have lost that object for ever. 

All boost APIs explicitly mention about their exception guarantees in the documentation.


No comments:

Post a Comment