Error Handling with Exceptions: Why Exceptions
By Dave Brownell | October 28, 2010
This article is part of a longer running series on Error Handling with Exceptions.
In today’s landscape, there are two primary means of handling errors within an application: exceptions and error codes. Working under the assumption that a clear and consistent error handling strategy is a Good Thing, the following is a brief list of why exceptions are a superior strategy when working within languages that support them.
Note that this article attempts to justify why exceptions represent a superior error handling strategy over the use of error codes. I understand that we are reaching the point where some readers may not have any experience with error codes at all. If you are one of these young pups, feel free to skip ahead.
Exceptions Represent the Path of Least Resistance
In many of today’s popular languages, exceptions are the preferred error handling strategy. This preference may be directly communicated in coding guidelines or implied by a standard/base class library that fundamentally throws (Link 1, Link 2). It is futile to resist the use exceptions under these circumstances (I speak from experience, as I foolishly tried). In some languages, there are means by which a developer can disable the use of exceptions in some specific scenarios, but this action generally has far reaching ramifications and amounts to throwing (HA!) the baby out with the bath water.
When working with a language that throws, it is generally easier (and likely better) to embrace the use of exceptions; a developer will be forced to deal with exceptions anyway, and a mixed-mode error handling strategy is just about as bad as no error handling strategy at all.
Exceptions Lead Towards the Path of Correctness
Any tool or technique that relieves a developer from the requirement of perfection is a good thing; techniques that rely on developer discipline have a higher potential rate of failure than those that don’t. Error handling with exceptions eliminates the need for developer discipline by requiring that error conditions be handled (even if “handled” means an application crash (while such crashes represent unacceptable behavior, it is better than ignoring error conditions (intentional or otherwise))). Most developers can tell the story of broken user experiences caused by an error code dropped in one line of one function called in one place, which resulted in the program continuing, blissfully unaware that something had gone wrong. Exceptions eliminate this entire category of developer error by construction.
Exceptions Facilitate the Creation of Cleaner Code
The following two code examples are functionally equivalent. The first example uses error codes to communicate errors while the second uses exceptions. Even though the examples are contrived, I have experienced the very same increases in code clarity when extrapolating the same modifications over larger code bases.
Error Codes:
// Prototypes HRESULT GetObject(int someCriteria, Object *pObject); HRESULT Function1(void); HRESULT Function2(void); HRESULT MyFunction(int someCriteria) { HRESULT hr; if(FAILED(hr = Function1())) return(hr); if(FAILED(hr = Function2())) return(hr); Object temporary; if(FAILED(hr = GetObject(someCrieria, &temporary))) return(hr); if(FAILED(hr = temporary.Method())) return(hr); // ... }
Exceptions:
// Prototypes Object GetObject(int someCriteria); void Function1(void); void Function2(void); void MyFunction(int someCriteria) { Function1(); Function2(); GetObject(someCriteria).Method(); // ... }
Exceptions Enable the Creation of “Complete” Objects
Errors may be encountered when creating an object. Without exceptions, all operations that the potential to fail are placed in a Construct-like method within a pattern called Two Phase Construction. In this pattern, “safe” operations are placed in the object’s constructor and “unsafe” operations are placed in a second method. It is the user’s responsibility to create the object, call the construct method, check for errors, and discontinue use of the object if errors were encountered during the second phase of construction.
This pattern is problematic because:
- It relies on developer discipline (exerted by the caller) to correctly follow the create/construct/check/discontinue use pattern every time an object is created
- It relies on developer discipline (exerted by the object’s author) to correctly check for a properly initialized object in every method
Fortunately, exceptions deprecate this pattern in that all construction activities can be placed in the constructor; potential errors are propagated to the user in the form of an exception. The result of this is that an object is either fully created or it isn’t; the caller isn’t required to make additional calls and the object’s author isn’t required to check for meaningless state information).
There is no good reason to use the Two Phase Construction Pattern in languages that support exceptions.
Exceptions can Contain Contextual Information
Unlike error codes, exceptions can be created to contain rich contextual information about the error and when and where it happened. This information may be about the cause of the error (the name of the file that couldn’t be opened), about the location in which the error was encountered (the call stack when the exception was thrown), or conditions that contributed to the error (caught exceptions that contributed to the current error (child exceptions)). While such mechanisms are possible with error codes (Link 1, Link 2), they are custom, often kludgy, rarely thread-safe, and more cumbersome to use than their exception counterparts.
Exceptions are Hierarchical
Unlike most error codes, exceptions can be organized into logical hierarchies. These hierarchies greatly increase a developer’s ability to selectively continue operations when error conditions are encountered. For example, some designs may dictate that one hierarchy of exceptions are fatal while another are not. By catching by base class rather than specific exception type, the design is open to future enhancement. Such hierarchical organization is extremely difficult to achieve with error codes.
Leave Your Comment