mirror of
https://github.com/boostorg/website2022.git
synced 2026-01-19 04:52:07 +00:00
This is based off of revision 56094d2c9f510f5ee4f1ffd4eb6b73788aaa62d7
of https://github.com/boostorg/website/.
URL of exact commit is: 56094d2c9f
268 lines
9.5 KiB
HTML
268 lines
9.5 KiB
HTML
---
|
|
title: Error and Exception Handling
|
|
copyright: David Abrahams 2001-2003.
|
|
revised:
|
|
---
|
|
|
|
|
|
Error and Exception Handling
|
|
|
|
|
|
|
|
Error and Exception Handling
|
|
============================
|
|
|
|
References
|
|
----------
|
|
|
|
|
|
The following paper is a good introduction to some of the
|
|
issues of writing robust generic components:
|
|
>
|
|
> [D. Abrahams:
|
|
> ``Exception Safety in Generic Components''](/community/exception_safety.html), originally
|
|
> published in [M. Jazayeri, R. Loos, D. Musser (eds.): Generic Programming,
|
|
> Proc. of a Dagstuhl Seminar, Lecture Notes on Computer
|
|
> Science. Volume. 1766](https://doi.org/10.1007/3-540-39953-4)
|
|
>
|
|
>
|
|
>
|
|
|
|
|
|
Guidelines
|
|
----------
|
|
|
|
|
|
### When should I use exceptions?
|
|
|
|
|
|
The simple answer is: ``whenever the semantic and
|
|
performance characteristics of exceptions are
|
|
appropriate.''
|
|
|
|
|
|
An oft-cited guideline is to ask yourself the question ``is
|
|
this an exceptional (or unexpected) situation?'' This guideline
|
|
has an attractive ring to it but is usually a mistake. The
|
|
problem is that one person's ``exceptional'' is another's
|
|
``expected'': when you look at the terms carefully, the
|
|
distinction evaporates and you're left with no guideline. After
|
|
all, if you check for an error condition, then in some sense
|
|
you expect it to happen, or the check is wasted code.
|
|
|
|
|
|
A more appropriate question to ask is: ``do we want stack
|
|
unwinding here?'' Because actually handling an exception is
|
|
likely to be significantly slower than executing mainline code,
|
|
you should also ask: ``Can I afford stack unwinding here?'' For
|
|
example, a desktop application performing a long computation
|
|
might periodically check to see whether the user had pressed a
|
|
cancel button. Throwing an exception could allow the operation
|
|
to be canceled gracefully. On the other hand, it would
|
|
probably be inappropriate to throw and *handle* exceptions
|
|
in the inner loop of this computation because that could have a
|
|
significant performance impact. The guideline mentioned above
|
|
has a grain of truth in it: in time-critical code, throwing an
|
|
exception should *be* the exception, not the rule.
|
|
|
|
|
|
### How should I design my exception classes?
|
|
|
|
|
|
1. **Derive your exception class from
|
|
`std::exception`**. Except in \*very\* rare
|
|
circumstances where you can't afford the cost of a virtual
|
|
table, `std::exception` makes a reasonable
|
|
exception base class, and when used universally, allows
|
|
programmers to catch "everything" without resorting to
|
|
`catch(...)`. For more about
|
|
`catch(...)`, see below.
|
|
2. **Use *virtual* inheritance.** This
|
|
insight is due to Andrew Koenig. Using virtual inheritance
|
|
from your exception's base class(es) prevents ambiguity
|
|
problems at the catch-site in case someone throws an
|
|
exception derived from multiple bases which have a base
|
|
class in common:
|
|
|
|
```
|
|
|
|
#include <iostream>
|
|
struct my\_exc1 : std::exception { char const\* what() const throw(); };
|
|
struct my\_exc2 : std::exception { char const\* what() const throw(); };
|
|
struct your\_exc3 : my\_exc1, my\_exc2 {};
|
|
|
|
int main()
|
|
{
|
|
try { throw your\_exc3(); }
|
|
catch(std::exception const& e) {}
|
|
catch(...) { std::cout << "whoops!" << std::endl; }
|
|
}
|
|
|
|
```
|
|
The program above prints `"whoops"` because the C++ runtime
|
|
can't resolve which `exception` instance to match in the first
|
|
catch clause.
|
|
3. ***Don't* embed a std::string object** or
|
|
any other data member or base class whose copy constructor
|
|
could throw an exception. That could lead directly to
|
|
std::terminate() at the throw point. Similarly, it's a bad
|
|
idea to use a base or member whose ordinary constructor(s)
|
|
might throw, because, though not necessarily fatal to your
|
|
program, you may report a different exception than intended
|
|
from a *throw-expression* that includes construction
|
|
such as:
|
|
|
|
```
|
|
|
|
throw some\_exception();
|
|
|
|
```
|
|
|
|
There are various ways to avoid copying string objects
|
|
when exceptions are copied, including embedding a
|
|
fixed-length buffer in the exception object, or managing
|
|
strings via reference-counting. However, consider the next
|
|
point before pursuing either of these approaches.
|
|
4. **Format the `what()` message on
|
|
demand**, if you feel you really must format the
|
|
message. Formatting an exception error message is typically a
|
|
memory-intensive operation that could potentially throw an
|
|
exception. This is an operation best delayed until after
|
|
stack unwinding has occurred, and presumably, released some
|
|
resources. It's a good idea in this case to protect your
|
|
`what()` function with a `catch(...)`
|
|
block so that you have a fallback in case the formatting code
|
|
throws
|
|
5. **Don't worry *too* much about the
|
|
`what()` message**. It's nice to have a
|
|
message that a programmer stands a chance of figuring out,
|
|
but you're very unlikely to be able to compose a relevant and
|
|
*user*-comprehensible error message at the point an
|
|
exception is thrown. Certainly, internationalization is
|
|
beyond the scope of the exception class author. [Peter Dimov](/users/people/peter_dimov.html) makes an
|
|
excellent argument that the proper use of a
|
|
`what()` string is to serve as a key into a table
|
|
of error message formatters. Now if only we could get
|
|
standardized `what()` strings for exceptions
|
|
thrown by the standard library...
|
|
6. **Expose relevant information about the cause of
|
|
the error** in your exception class' public interface.
|
|
A fixation on the `what()` message is likely to
|
|
mean that you have neglected to expose information someone might
|
|
need, in order to make it a coherent message for users. For
|
|
example, if your exception reports a numeric range error,
|
|
it's important to have the actual numbers involved available
|
|
*as numbers* in the exception class' public interface
|
|
where error reporting code can do something intelligent with
|
|
them. If you only expose a textual representation of those
|
|
numbers in the `what()` string, you will make life
|
|
very difficult for programmers who need to do something more
|
|
(e.g. subtraction) with them than dumb output.
|
|
7. **Make your exception class immune to
|
|
double-destruction** if possible. Unfortunately,
|
|
several popular compilers occasionally cause exception
|
|
objects to be destroyed twice. If you can arrange for that to
|
|
be harmless (e.g. by zeroing deleted pointers) your code will
|
|
be more robust.
|
|
|
|
|
|
### What About Programmer Errors?
|
|
|
|
|
|
As a developer, if I have violated a precondition of a
|
|
library I'm using, I don't want stack unwinding. What I want is
|
|
a core dump or the equivalent - a way to inspect the state of
|
|
the program at the exact point where the problem was detected.
|
|
That usually means `assert()` or something like
|
|
it.
|
|
|
|
|
|
Sometimes it is necessary to have resilient APIs which can
|
|
stand up to nearly any kind of client abuse, but there is
|
|
usually a significant cost to this approach. For example, it
|
|
usually requires that each object used by a client be tracked
|
|
so that it can be checked for validity. If you need that sort
|
|
of protection, it can usually be provided as a layer on top of
|
|
a simpler API. Beware half-measures, though. An API that
|
|
promises resilience against some, but not all abuse is an
|
|
invitation to disaster. Clients will begin to rely on the
|
|
protection and their expectations will grow to cover
|
|
unprotected parts of the interface.
|
|
|
|
|
|
**Note for Windows developers**: unfortunately,
|
|
the native exception-handling used by most Windows compilers
|
|
actually throws an exception when you use
|
|
`assert()`. Actually, this is true of other
|
|
programmer errors such as segmentation faults and
|
|
divide-by-zero errors. One problem with this is that if you use
|
|
JIT (Just In Time) debugging, there will be collateral
|
|
exception-unwinding before the debugger comes up because
|
|
`catch(...)` will catch these not-really-C++
|
|
exceptions. Fortunately, there is a simple but little-known
|
|
workaround, which is to use the following incantation:
|
|
```
|
|
|
|
extern "C" void straight\_to\_debugger(unsigned int, EXCEPTION\_POINTERS\*)
|
|
{
|
|
throw;
|
|
}
|
|
extern "C" void (\*old\_translator)(unsigned, EXCEPTION\_POINTERS\*)
|
|
= \_set\_se\_translator(straight\_to\_debugger);
|
|
|
|
```
|
|
|
|
This technique doesn't work if the SEH is raised from within
|
|
a catch block (or a function called from within a catch block),
|
|
but it still eliminates the vast majority of JIT-masking
|
|
problems.
|
|
|
|
|
|
### How should I handle exceptions?
|
|
|
|
|
|
Often the best way to deal with exceptions is to not handle
|
|
them at all. If you can let them pass through your code and
|
|
allow destructors to handle cleanup, your code will be
|
|
cleaner.
|
|
|
|
|
|
#### Avoid `catch(...)` when
|
|
possible
|
|
|
|
Unfortunately, operating systems other than
|
|
Windows also wind non-C++ "exceptions" (such as thread
|
|
cancellation) into the C++ EH machinery, and there is sometimes
|
|
no workaround corresponding to the
|
|
`_set_se_translator` hack described above. The
|
|
result is that `catch(...)` can have the effect of
|
|
making some unexpected system notification at a point where
|
|
recovery is impossible to look just like a C++ exception thrown
|
|
from a reasonable place, invalidating the usual safe
|
|
assumptions that destructors and catch blocks have taken valid
|
|
steps to ensure program invariants during unwinding.
|
|
|
|
I reluctantly concede this point to Hillel Y. Sims, after
|
|
many long debates in the newsgroups: until all OSes are
|
|
"fixed", if every exception were derived from
|
|
`std::exception` and everyone substituted
|
|
`catch(std::exception&)` for
|
|
`catch(...)`, the world would be a better place.
|
|
|
|
|
|
Sometimes, `catch(...)`, is still the most
|
|
appropriate pattern, in spite of bad interactions with
|
|
OS/platform design choices. If you have no idea what kind of
|
|
exception might be thrown and you really *must* stop
|
|
unwinding it's probably still your best bet. One obvious place
|
|
where this occurs is at language boundaries.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|