Oct 16, 2004

Using Exceptions...

A little while ago, I was talking about exceptions. During this discussion, I was going to say the old adage that exceptions are only to be used for exceptional cases. In Sutters Mill #31, “When and How to Use Exceptions,” from the August issue of C++ Users Journal, Herb Sutter provides a clear, objective, and measurable answer of when and how to use the exceptions.

In this discussion, Herb points out that most languages created in the last twenty years have exceptions in them, and there is a good reason for this. Without exceptions, it is a little too easy to simply ignore errors, but with exceptions, to ignore them, you must write code to explicitly ignore them, and as such, you are less likely to write such code (and it is more likely to be caught in a code-review).

In the above, I mention that it is easy to ignore errors, but I did not mention what errors are. Herb defines errors as preconditions, postconditions, and invariants that are either not met or cannot be met, which Design-by-Contract comes to mind, and is strict in stating that any other causes are not errors, and should not be using exception constructs. As assert is not error handling and ignoring errors is not acceptable, your choices to report errors is to use errors codes or exceptions. Herb mentions the following points in favour of exceptions:

By contrast, Herb mentions that you should only consider error codes when none of the benefits of exceptions apply directly, when you are providing a library with either mixed compilers or mixed languages, and when profiling shows that they occupy too much time, although he does point out that this is usually because the exceptions being launched are not truly errors after all.

Herb reminds us that the standard library uses exceptions, and therefore, you are going to be exposed to exceptions whether you want to or not, but the advantage here is that compiler vendors are optimizing for them, since they are used.

With either error codes or exceptions, the program should be left in a valid state, which is called a basic guarantee, and is especially important is releasing resources. If you can, you should likewise prefer to guarantee that the state of the program will either be the original state in the event of a failure or the intended state state when the function completes, which is known as a strong guarantee. Finally, if your function allows, you should prefer to guarantee that a function will never fail, such as destructors and swap functions.

The article provides a lot of good examples and a lot more discussion than the above. It definitely is worth a read.

One of the things that I mentioned above deals with performance. In the same issue, Andrew Koenig and Barbara E. Moo measure the performance between a vector, list, and deque, and they observed that vector::reserve on medium-sized vectors increased performance, but on small or large vectors, this was not the case, appending elements to a list was almost 40 times longer than appending the element to an identical deque, and that inserting elements at the beginning of a list takes longer than appending them to the end.

In the end, they recommend the same advice as others, in that vectors should be used except if there is a better container, such as if you are inserting at the beginning of the container, you will probably want a deque instead. They also recommend using reserve, as does Effective STL.

But the point that really ties into the above is their second conclusion, which is that if you care about performance, you should measure it yourself.

Filed In