diff options
Diffstat (limited to 'more/error_handling.html')
-rw-r--r-- | more/error_handling.html | 240 |
1 files changed, 240 insertions, 0 deletions
diff --git a/more/error_handling.html b/more/error_handling.html new file mode 100644 index 0000000000..14364048f5 --- /dev/null +++ b/more/error_handling.html @@ -0,0 +1,240 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2//EN"> + +<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:</p> + + <blockquote> + <a href="generic_exception_safety.html">D. Abrahams: ``Exception Safety + in Generic Components''</a>, originally published in <a href= + "http://www.springer.de/cgi-bin/search_book.pl?isbn=3-540-41090-2">M. + Jazayeri, R. Loos, D. Musser (eds.): Generic Programming, Proc. of a + Dagstuhl Seminar, Lecture Notes on Computer Science. Volume. 1766</a> + </blockquote> + + <h2>Guidelines</h2> + + <h3>When should I use exceptions?</h3> + + <p>The simple answer is: ``whenever the semantic and performance + 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 + ring to it, but is usually a mistake. The problem is that one person's + ``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.</p> + + <p>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 + 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. The guideline mentioned above has a grain of + truth in it: in time critical code, throwing an exception + should <em>be</em> the exception, not the rule.</p> + + <h3>How should I design my exception classes?</h3> + + <ol> + <li><b>Derive your exception class + 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><b>Use <i>virtual</i> inheritance.</b> 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: + +<pre> +#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; } +} +</pre> + +The program above prints <code>"whoops"</code> because the +C++ runtime can't resolve which <code>exception</code> instance to +match in the first catch clause. + + </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 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 <i>throw-expression</i> that includes construction 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 message formatters. Now if only we could get + standardized <code>what()</code> strings for exceptions thrown by the + standard library...</li> + + <li><b>Expose relevant information about the cause of the error</b> in + your exception class' public interface. A fixation on the + <code>what()</code> message is likely to mean that you neglect to + expose information someone might need in order to make 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 <i>as numbers</i> 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 + <code>what()</code> string, you will make life very difficult for + programmers who need to do something more (e.g. subtraction) with them + than dumb output.</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> + + <p>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 <tt>assert()</tt> or + something like it.</p> + + <p>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 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 + 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> +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); +</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. + + <p>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 <code>std::exception</code> and + everyone substituted + <code>catch(std::exception&)</code> for <code>catch(...)</code>, the + world would be a better place.</p> + + <p>Sometimes, <code>catch(...)</code>, 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 + <i>must</i> stop unwinding it's probably still your best bet. One obvious + place where this occurs is at language boundaries.</p> + <hr> + + <p>© Copyright David Abrahams 2001-2003. All rights reserved.</p> + + <p>Revised + <!--webbot bot="Timestamp" s-type="EDITED" s-format="%d %B, %Y" startspan --> + 21 August, 2003<!--webbot bot="Timestamp" endspan i-checksum="34359" --> + </p> + </body> +</html> + |