updated guidelines

[SVN r18047]
This commit is contained in:
Dave Abrahams 2003-03-22 14:38:03 +00:00
parent 7523019953
commit 70fc63b56b

View File

@ -1,15 +1,22 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2//EN">
<meta name="generator" content="HTML Tidy, see www.w3.org">
<meta http-equiv="Content-Type" content="text/html; charset=windows-1252">
<html>
<head>
<meta name="generator" content=
"HTML Tidy for Cygwin (vers 1st April 2002), see www.w3.org">
<meta http-equiv="Content-Type" content=
"text/html; charset=windows-1252">
<title>Error and Exception Handling</title>
</head>
<body>
<h1>Error and Exception Handling</h1>
<h2>References</h2>
<p>The following paper is a good introduction to some of the issues of
writing robust generic components:
writing robust generic components:</p>
<blockquote>
<a href="generic_exception_safety.html">D. Abrahams: ``Exception Safety
@ -24,7 +31,7 @@
<h3>When should I use exceptions?</h3>
<p>The simple answer is: ``whenever the semantic and performance
characteristics of exceptions are appropriate.''
characteristics of exceptions are appropriate.''</p>
<p>An oft-cited guideline is to ask yourself the question ``is this an
exceptional (or unexpected) situation?'' This guideline has an attractive
@ -32,7 +39,7 @@
``exceptional'' is another's ``expected'': when you really 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.
sense you expect it to happen, or the check is wasted code.</p>
<p>A more appropriate question to ask is: ``do we want stack unwinding
here?'' Because actually handling an exception is likely to be
@ -40,9 +47,70 @@
``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 cancelled gracefully. On the other hand, it would probably
be inappropriate to throw and <i>handle</i> exceptions in the inner loop of
this computation because that would have a significant performance impact.
operation to be cancelled gracefully. On the other hand, it would
probably be inappropriate to throw and <i>handle</i> exceptions in the
inner loop of this computation because that could have a significant
performance impact.</p>
<h3>How should I design my exception classes?</h3>
<ol>
<li><b>Inherit from <code>std::exception</code></b>. Except in *very*
rare circumstances where you can't afford the cost of a virtual table,
<code>std::exception</code> makes a reasonable exception base class,
and when used universally, allows programmers to catch "everything"
without resorting to <code>catch(...)</code>. For more about
<code>catch(...)</code>, see below.</li>
<li>
<b><i>Don't</i> embed a std::string object</b> or any other data
member or base class whose copy constructor could throw an exception.
That could lead to termination during stack unwinding. Similarly,
it's a bad idea to use a base or member whose ordinary constructor
might throw, because, though not fatal, you will report a different
exception than intended when that happens in a
<i>throw-expression</i> such as:
<blockquote>
<pre>
throw some_exception();
</pre>
</blockquote>
<p>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.</p>
</li>
<li><b>Format the <code>what()</code> message on demand</b>, 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
<code>what()</code> function with a <code>catch(...)</code> block so
that you have a fallback in case the formatting code throws</li>
<li><b>Don't worry <i>too</i> much about the <code>what()</code>
message</b>. 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 <i>user</i>-comprehensible error message at the point an
exception is thrown. Certainly, internationalization is beyond the
scope of the exception class author. <a href=
"../people/peter_dimov.htm">Peter Dimov</a> makes an excellent argument
that the proper use of a <code>what()</code> string is to serve as a
key into a table of error messages. Now if only we could get
standardized <code>what()</code> strings for exceptions thrown by the
standard library...</li>
<li><b>Make your exception class immune to double-destruction</b> 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.</li>
</ol>
<h3>What About Programmer Errors?</h3>
@ -50,26 +118,27 @@
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 <tt>assert()</tt> or
something like it.
something like it.</p>
<p>Sometimes it is neccessary 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 which 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.
<p>Sometimes it is neccessary 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 which
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.</p>
<p><b>Note for Windows developers</b>: unfortunately, the native
exception-handling used by most Windows compilers actually throws an
exception when you use <tt>assert()</tt>. 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.
Fortunately, there is a simple but little-known workaround, which is to use
the following incantation:
there will be collateral exception-unwinding before the debugger comes up
because <code>catch(...)</code> will catch these not-really-C++
exceptions. Fortunately, there is a simple but little-known workaround,
which is to use the following incantation:</p>
<blockquote>
<pre>
@ -81,12 +150,43 @@ extern "C" void (*old_translator)(unsigned, EXCEPTION_POINTERS*)
= _set_se_translator(straight_to_debugger);
</pre>
</blockquote>
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.
<h3>How should I handle exceptions?</h3>
<p>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.</p>
<h4>Avoid <code>catch(...)</code> when possible</h4>
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
<code>_set_se_translator</code> hack described above. The result is that
<code>catch(...)</code> can have the effect of making some unexpected
system notification at a point where recovery is impossible 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, who beat it into me (&lt;wink&gt;):
until all OSes are "fixed", if every exception were derived from
<code>std::exception</code> and everyone substituted
<code>catch(std::exception&amp;)</code> for <code>catch(...)</code>, the
world would be a better place.
<hr>
<p>&copy; Copyright David Abrahams 2001. Permission to copy, use, modify,
sell and distribute this document is granted provided this copyright notice
appears in all copies. This document is provided "as is" without express or
implied warranty, and with no claim as to its suitability for any purpose.
sell and distribute this document is granted provided this copyright
notice appears in all copies. This document is provided "as is" without
express or implied warranty, and with no claim as to its suitability for
any purpose.</p>
<p>Revised
<!--webbot bot="Timestamp" s-type="EDITED" s-format="%d %B, %Y" startspan -->19 August, 2001<!--webbot bot="Timestamp" endspan i-checksum="34359" -->
<!--webbot bot="Timestamp" s-type="EDITED" s-format="%d %B, %Y" startspan -->
22 March, 2003<!--webbot bot="Timestamp" endspan i-checksum="34359" -->
</p>
</body>
</html>