TITLE: a case against exception specifications (Newsgroups: comp.lang.c++.moderated,comp.lang.c++,comp.std.c++, 22 Sep 98) GLASSBOROW Francis Glassborow > Now the problem is persuading the majority that they should provide > exception specs (it is so easy not to). Typically I think that a member > function should be one of the following (more refined if you insist, but > at least) > > throw() // throw nothing, e.g. destructors > throw(std::exception) // only throws exceptions rooted in the standard > > // exception hierarchy > throw(MyBaseClassException) // allexceptions are derived from one > // provided in the base class > throw(std::exception, MyBaseClassException) ABRAHAMS: abrahams@motu.com (Dave Abrahams) I disagree with Mr. Glassborow's recommendation in the strongest terms. In fact, there is an excellent argument that exception-specifications have no place at all in production code (I didn't invent that - it came from Taligent's excellent guide to programming). The problem is that the run-time effects of an exception specification are almost never what you wanted (usually immediate termination), and in complex systems it is almost impossible to maintain exception specifications so that they are always up-to-date. For example, imagine a function f(): X f( const X& x, const Y& y ) throw (e1, e2) { Z z = x + y; myStream << z; z.g( x ); return z; } Many functions are called in the course of the execution of f(). Any one might need to be modified during the life of a project to throw some exception which it did not throw previously. Worse, many of these functions are not named distinctly in the body of f(), because they are operators or implicit conversions which could not easily be found by searching the source code. If a f ()'s exception-specification isn't properly updated, you have a catastrophe. But if it is updated, what is the result of your painstaking maintenance? The answer: absolutely nothing. Exception-specifications have no compile-time behavior. Aside from optimizations which the compiler can potentially make, an exception-specification has no runtime behavior unless it is violated, in which case the result is seldom useful. Personally, I would only ever consider using an empty exception-specification (i.e. throw() ), and then only whent the function it adorns is both trivial enough that I have reasonable confidence that I can maintain it, and critical enough to the execution-speed of my program that optimizing it is important. GLASSBOROW: > Now users of your class have a fighting chance of knowing what they have > to handle. Of course MI etc might increase the list, but generally > exceptions thrown by sub-objects should be handled locally before > throwing, if necessary, a class based exception. ABRAHAMS: In my experience, one very seldom needs or wants to handle different exceptions differently. Code quickly becomes very complicated when you constantly try to handle an out-of-memory condition differently from a disk full error. The only places this comes up in my code are at the point of error reporting and when there is some kind of specialized cache which can be released in order to retry the operation. The latter is extremely rare. For the former, you only need to write one error-reporting function, and catch all exceptions with catch(...). The error-reporting function takes the following general form: void ReportCurrentException() { try { throw; // rethrows the exception currently being handled. } catch( const e1& ) { // Handle the exceptions we know about // report e1 error } catch( const e2& ) { // report e2 error } catch( const e3& ) { // report e3 error } ... catch( ... ) { // Handle any exceptions we don't know about (just in case) // report unknown error } }