Questions About Exception Specifications

In the past few days, I’ve had several people independently send me email to ask related questions about exception specifications. That must be reason enough to echo some answers here.

Background

For background, here are two places where I’ve written about exception specifications:

I’ve taken the liberty of pasting the entire latter reference at the bottom of this post. Enjoy!

Now on to Question the First…

Q1: How can you know you’ve caught everything if you don’t use exception specifications?

Andrew Skypeck asked:

In CCS#75 you suggest that one ought not provide an exception specifications with functions. Without them, how can a user of the function be certain they have caught all possible exceptions without inspecting the code they are calling? This is often not possible for API users that have header files only.

Two thoughts:

First, the way to catch everything is to catch(…), and that always works. You seem to be wanting to use exception specifications as documentation (which is fine), and so you can get the same ability by writing the exception specification as a comment. Note that even if you try to write them all as actual code, you can’t do it in general — notably you can’t do it for templates, because if a template can be instantiated with any type, you generally can’t know in advance what it might throw.

Second, the caller of a function doesn’t always need to catch all possible exceptions; it should only catch the exceptions that it knows how to handle (or translate). Exception safety is not just about making sure that the code that knows how to detect an error throws, and that the code that knows how to handle the error catches; rather, in practice exception safety is largely about making sure the code in between stays out of the way, including that it doesn’t catch and absorb exceptions. For more about this aspect of exception safety, start with these Items in C++ Coding Standards, and follow the references at the end of each Item to dig into the details that interest you:

  • 70. Distinguish between errors and non-errors.
  • 71. Design and write error-safe code.
  • 72. Prefer to use exceptions to report errors.
  • 74. Report, handle, and translate errors appropriately.

Which brings us to Question the Second:

Q2: Why not statically enforce exception specifications?

Bob Rossi asked, while kindly citing the above article:

I would like to know what the possibilities are of C++0x enforcing the exception specifications like Java does. Here is a good article describing why exception specifications acting the way they do are currently bad, http://www.gotw.ca/publications/mill22.htm

I must not fully understand the situation. What is wrong with the compiler time checks that Java does to enforce the exception specifications? I find this feature extremely useful as a developer!

The short answer is that nobody knows how to fix exception specifications in any language, because the dynamic enforcement C++ chose has only different (not greater or fewer) problems than the static enforcement Java chose.

If you’re interested in reading more about this, try googling for newsgroup postings, http://groups.google.com/groups?q=sutter+exception+specifications. The following is taken from the second hit, which I wrote last fall:

Essentially, exception specifications are a wonderful idea in basic principle, but no one really knows how to design them.

There are two major approaches, and both have serious problems. Java chose static enforcement (at compile time, as suggested above), and C++ chose dynamic enforcement. Interestingly, I see Java people ask "why not enforce these dynamically?" about as often as I see C++ people ask "why not enforce these statically?"

Briefly:

When you go down the Java path, people love exception specifications until they find themselves all too often encouraged, or even forced, to add throws Exception, which immediately renders the exception specification entirely meaningless. (Example: Imagine writing a Java generic that manipulates an arbitrary type T…)

When you go down the C++ path, people love exception specifications until they discover that violating one means invoking terminate, which is almost never what you want.

So why did C++ do exception specifications the way it did, with dynamic enforcement via terminate? And why did Java do it the other way? In both cases, it seemed like a good idea that avoided some set of known problems, and you can’t always know the new problems you incur instead until you gain experience with multiple releases of large code bases, which means over time.

The future of exception specifications?

Bob responded by asking why we don’t just fix exception specifications, or remove them entirely until something better comes along. Those are good questions.

Taking the latter part first: As tempting as it may be, one can’t just rip out a language feature because one decides it isn’t working out quite right. The primary reason is that there are people using the feature, and breaking existing legal code is, well, rude.

What about fixing exception specifications? That would be nice, and I think nearly everyone I know is willing to consider changing them, but only if: (a) there is a well-understood replacement (there isn’t today), and (b) there was a good way to deal with migration from existing code which may rely on the existing feature.

Deliberate change with a strong dose of backward compatibility is always the key. This one needs more deliberation, at least for now, and isn’t expected to change in C++0x.

Epilogue: C++CS Item 75

I’m sure Addison-Wesley won’t mind overmuch if I paste one Item here in its entirety. It has been slightly reformatted for the web.

75. Avoid exception specifications.

Summary

Take exception to these specifications: Don’t write exception specifications on your functions unless you’re forced to (because other code you can’t change has already introduced them; see Exceptions).

Discussion

In brief, don’t bother with exception specifications. Even experts don’t bother. The main problems with exception specifications are that they’re only “sort of” part of the type system, they don’t do what most people think, and you almost always don’t want what they actually do.

Exception specifications aren’t part of a function’s type, except when they are. They form a shadow type system whereby writing an exception specification is variously:

  • Illegal: In a typedef for a pointer to function.
  • Allowed: In the identical code without the typedef.
  • Required: In the declaration of a virtual function that overrides a base class virtual function that has an exception specification.
  • Implicit and automatic: In the declaration of the constructors, assignment operators, and destructors when they are implicitly generated by the compiler.

A common but nevertheless incorrect belief is that exception specifications statically guarantee that functions will throw only listed exceptions (possibly none), and enable compiler optimizations based on that knowledge.

In fact, exception specifications actually do something slightly but fundamentally different: They cause the compiler to inject additional run-time overhead in the form of implicit try/catch blocks around the function body to enforce via run-time checking that the function does in fact emit only listed exceptions (possibly none), unless the compiler can statically prove that the exception specification can never be violated in which case it is free to optimize the checking away. And exception specifications can both enable and prevent further compiler optimizations (besides the inherent overhead already described); for example, some compilers refuse to inline functions that have exception specifications.

Worst of all, however, is that exception specifications are a blunt instrument: When violated, by default they immediately terminate your program. You can register an unexpected_handler, but it’s highly unlikely to help you much because you get exactly one global handler and the only way the handler could avoid immediately calling terminate would be to rethrow an exception that is permissible—but because you have only one handler for your whole application, it’s hard to see how it could do useful recovery or usefully know what exceptions might be legal without trivializing exception specifications altogether (e.g., following the discipline of having all exception specifications allow some general UnknownException eliminates any advantage that having an exception specification might have had in the first place).

You generally can’t write useful exception specifications for function templates anyway, because you generally can’t tell what exceptions the types they operate on might throw.

Paying a performance overhead in exchange for enforcements that are nearly always useless because they are fatal if they ever fire is an excellent example of a premature pessimization (see Item 9).

These is no easy fix for the problems described in this Item. In particular, the problems are not easily solved by switching to static checking. People often suggest switching from dynamically checked exception specifications to statically checked ones, as provided in Java and other languages. In short, that just trades one set of problems for another; users of languages with statically checked exception specifications seem to equally often suggest switching to dynamically checked ones.

Exceptions

If you have to override a base class virtual function that already has an exception spec­ifi­ca­tion (e.g., ahem, std::exception::what), and you don’t have the ability to change the class to remove the exception specifications (or to convince the class’s main­tainer to remove them), you will have to write a compatible exception specification on your overriding function, and you should prefer to make it no less restrictive than the base version so as to minimize the frequency with which it might be violated:

// in a class written by someone else,
// the author used an exception specification,
// and if you can’t get him to remove it…

class Base { // …
virtual f() throw( X, Y, Z );
};

// … then in your own class your override
// must have a compatible (and preferably
// the identical) exception specification
class MyDerived : public Base { // …
  virtual f() throw( X, Y, Z );
};

[BoostLRG]’s experience is that a throws-nothing exception specification (i.e., throw()) on a non-inline function “may have some benefit with some compilers.” Not a stunning endorsement from one of the most highly regarded and expertly designed C++ library projects in the world.

References [see the C++CS online bibliography for reference details and other links]

[BoostLRG] – [Stroustrup00] §14.1, §14.6 – [Sutter04] §13

4 thoughts on “Questions About Exception Specifications

  1. The issue isn’t quite so cut and dried in Java as presented in this blog.  It seems that C++ exceptions truely do suffer from a flawed implementation, which reminds me of some of the issues with Java generics – problems because of the compromise approach the designers chose.
    But in this blog is reiterated the typical, main argument against Java exceptions: too complicated for Java Joe.  That’s what I call a punt.
    Here’s some great reading (with some strong pro’s and other weak con’s, in addition to Java Joe’s programming ability) on the subject, which advocates a more middle of the road approach.
  2. I see why C++’s exceptions are bad, and I’ve experienced Java’s, but in both cases it appears to me as if it’s a case of trying to overuse the available and imperfect information.  I think Java’s system might be great, if it was something you could demote to a warning.

    Are they just glorified documentation then?  Depends on how far you take that, but I think they are far more useful than an unstructured comment.  Besides which it’s not if by chance someone finds the miraculous solution to strongly checked exceptions, those glorified comments might become even more useful (not crossing my fingers though).

Comments are closed.

In comments, use [code] [/code] for code blocks, and the same with angle brackets for code inside text paragraphs. I appreciate and try to acknowledge feedback on GotW articles; if you're suggesting an improvement for a GotW Solution post, please post under your real name (or include it in the comment text) if you would like to be acknowledged here and when the material is updated and expanded in future Exceptional C++ books.

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s