What is the right way to handle exceptions?
There are several ways to handle exceptions in a program. An exception, that is, a condition from which the program cannot recover locally, can be handled by checking the result of function, or using a try/catch mechanism (which can be simulated in C by a long jump).
Between these two extremes of the spectrum, a lot has been said and done. However, they represent not only technical options, but ways of thinking about how a program works.
The try/catch technique is currently well established; it is not the only option though. C programmers have always used return values as a way of expressing the status of the executing code.
The problem is that there is a lot of redundancy and inefficiency in the use of exceptions. To see this, consider two options from the many uses of exception based code: expected conditions and unexpected conditions.
Expected exceptions are the ones we know how to protect from. For example, it might be a memory exhaustion problem. In that case, we know where the issue is coming from, which means that it could be more economically handled using error codes.
Unexpected exceptions, on the other hand, are the ones that we don’t know how to recover from, mostly corresponding to bugs on some combination of software/hardware. Here, throwing an exception is useless, because while a bug is happening, the most you will get in terms of debugging information is a stack trace. On the other hand, a core dump generated by a language like C++ is much more informative – it shows not only the location, but also the contents of memory at the time of the crash. Exceptions, though easy to program, don’t let a core dump happen, because by definition they need to call all the destructors (in C++ at least), and execute required catch and finally blocks.
Objections to Error Codes
The standard objection to error codes is that you have to check them at every use. This is not true, however: one can create “check an cleanup versions” of every major function that returns an error code. Then, on error, the code calls a clean up function that can do the required “destruction” of objects. One can even install handlers that take care of cleaning up specific modules.
In fact, production code in C++ that uses exceptions does something similar. Exception catching is frequently isolated in a few modules that do the right thing, instead of just mindlessly receiving the exception and printing something.
What is best then? Exceptions are certainly easier to use, but impose a run time penalty that cannot be avoided (and consider that C++ tried really hard). Moreover, professional users know better than throwing and catching exceptions ad-hoc. So, the advantages in terms of functionality are really not clear.
A lot of production software in C++ doesn’t use exceptions (Chrome, for example). Exceptions are not reentrant in C++ (because of destructors). While in Java one cannot avoid them (built into the language), in C++ I believe one should think twice about using exceptions and the main way of handling error conditions.