... in systems programming.
Why is unmaintainable manual handling of returned error codes not a reason to use exceptions? For quick and dirty scripts, I agree that checking error codes is indeed easy to forget, or at least a chore when done diligently. But that's scripting - and there are scripting tasks that are better implemented in e.g. Python than in a systems programming language. At run time, when there is an error (in the form of an exception) that is not caught, the script will just die. And that is o.k., since one can simply fix the environment manually (edit a config file or data set, delete a directory hierarchy...) and re-run the script.
Of course, we can re-run program written in any programming language (including e.g. Java programs), and any program that uses exceptions will conveniently die on the first unhandled error. But the point is that this behaviour is mostly useful with scripting tasks. In longer running programs (i.e. systems) on the other hand, errors must be handled and the program must be able to go on without manual user intervention. So, it's better not to throw exceptions down the stack. While exceptions make code easier to combine on a mere syntactical level, they take control of the flow of execution, so the result is normally not what you want from a systems programming standpoint. On the other hand, examining the return code of each little function call can be impractical. If handling error codes in straight-line code is inconvenient, the solution is not exceptions, but an actual redesign in the program's architecture. Try keeping your state in a more global place (resource managers) and remembering at least the last error there. Example: OpenGL API.
Another reason why exceptions make your life hard is that they require a different syntax to handle them, which puts a tax on code interoperability. This is just like methods: (Depending on who you ask) they might make the code just slightly easier to read or write, at the cost of the incompatibility that is introduced when we need e.g. callbacks.
Another problem with exceptions is that they rely on object destructors, at least if you want to catch an exception at all. The object destructors contain the cleanup code for all the objects that go out of scope during stack unwinding. But having cleanup code in destructors makes your program hard to read because the thread of execution is flying all over the code base - in the best OOP manner. Either that, or you'll end up writing normal functions as objects + run() methods (when cleanup isn't as simple as cleaning up all the used objects separately). The whole situation gets only worse when the object destructors aren't executed in deterministic order (i.e. in garbage collected language like Java).
Another ergonomic problem with exceptions is that the creator of a function that throws exceptions is in the business of second-guessing what's an actual error for the caller. This is typically not possible due to missing context. For example a partial read (end-of-file?) might or might not be considered an error by the caller. While functions designed for return codes often make these classifications too (for example, by returning negative numbers for errors), it's much easier for the caller to override this decision when the set of possible outcomes is not arbitrarily partitioned in two by differing syntax and semantics, as is the case with exceptions. A good example would be Python's list.index() function which returns the index of a searched-for item in a list, or throws an exception if the item was not found. There are so many situations where a missing item is totally possible and expected as an outcome, and getting a return code of -1 would be much nicer to deal with than an exception.
An argument in favour of exception that is sometimes heard is that exceptions make it easier to handle exceptions at the appropriate place, meaning not at the immediate caller but maybe two or three levels up the call stack. The problem with this argument is that the context out of which errors arise is typically not known at the high-level places where exceptions are catched. There is often no proper way to deal with exceptions in these places, besides ignoring them or letting the program quit. There is a case to be made for using exceptions when running VM-like things in an all-or-nothing manner, i.e. "parse and interpret this file, quit with an exception if there are problems". But this, again, is more of a scripting like situation and not really relevant for complex software systems.
Created: 2017-11-17
Updated: 2021-02-28