Exception specifications

C++ provides a mechanism that allows any function to declare exactly which exception types it may throw, and these declarations are actually enforced in runtime. We will review exactly how this mechanism works, and why it is usually left unused.

Here’s the proper syntax for providing a list of exceptions that may be thrown during the execution of a specific function:

// may throw only type A or type B (or derived types):
void f () throw (A, B);

It is obviously possible to declare a function as nothrow, using empty parentheses:

// same as __declspec(nothrow):
void func () throw ();

When a specification is omitted, C++ assumes the function can throw anything.

As a side note, the exception specification is not considered as part of the function’s signature.

When inheriting, the derived function must not throw more than the base function: if base function could only throw x, the derived function may only throw (derived from) x or nothing. This is called covariance [ we will also demand covariance in returned values (why?) ].

Exception specification can not be enforced in compile time (for many good reasons), therefore it is enforced in run time. Every use of the previously defined f() is basically transformed by the compiler to something along the lines of:

try {
catch (const A &a) { throw; }
catch (const B &b) { throw; }
catch (...) { std::unexpected(); }

According to the standard (and as implemented above), the std::unexpected() function should be called when the exception specification is breached. It is possible to supply a user defined function to run instead of the default handler by invoking std::set_unexpected().

If the function is defined as nothrow, the compiler assumes you know what you’re doing and is able to optimize away all of this exception handling. So if the code actually does throw something.. you’re going to be in a very bad place. Never use a nothrow specification unless you’re 100% certain that it’s indeed the correct situation and that it will remain so in the future.

Exception specifications incur runtime overhead due to the fact that they are enforced at runtime, and don’t have much added value. Well, other than extending the documentation of the interface by including possible exceptions — which may be huge, but still not worth the runtime penalty. This may be why they usually remain unused.

However, using the empty throw() specification when appropriate is very important; Functions that provide nothrow exception safety, such as destructors and de-allocators, must be marked as such to allow extra compiler optimizations to take place.

Regarding the actual implementation of this feature on modern compilers, here’s what I came up with:

  • On Microsoft’s Visual Studio (2008),
    The only supported exception specification is the empty one. When you attempt to use a non-empty specification, the following warning is issued: “warning C4290: C++ exception specification ignored except to indicate a function is not __declspec(nothrow)”
  • On GNU G++ (4.3.3),
    Looks like the mechanism is implemented as required by the standard. When I threw a wrong exception (one that wasn’t in the throw() specification), std::terminate() was invoked through std::unexpected(), as required. Here is what I got at runtime (though not even a warning during compilation): “terminate called after throwing an instance of ‘C'”

9 thoughts on “Exception specifications

  1. While I like the idea of exception specification, I think it’s too much bureaucracy.
    Still, I disagree with your sentiment “which is huge in my eyes, but still not worth the runtime penalty”. What is the runtime penalty? Can you tell? Doesn’t it depend on whether or not the actual application you are developing is time critical?
    If it is indeed time critical, the desired behavior I’d want is to be able to have a compiler flag to disable/enable exception specifications.
    That way, you can enable them for tests and make sure you never[1] throw unexpected, and disable them for release builds.

    [1] as certain as your code coverage by tests allows you to be.

    1. The runtime penalty is more in cases where the function MAY throw, but doesn’t actually (for example when you’re implementing an interface or foreseeing inheritance), and the exception specification actually prevents the compiler from optimizing.
      For example, here’s why Boost doesn’t use exception specifications: http://www.boost.org/development/requirements.html#Exception-specification

      The solution you suggest (compiler flag) is esentially the same as treating the exceptions you expect to happen and putting an assert(0) on a catch all (catch (…)). Then the assert will be removed on release builds automatically while still providing the needed checking during test phase.

      However, I do believe that exception specifications are good as they provide a far better contract (that is also enforced) between the caller and the callee. The problem is in the fact that the enforcement is done in run time and not in compile time.. You would never want your program to terminate with an std::unexpected() in a released version.

    1. I actually can’t stress enough how seriously I dislike the way Java forces you to handle exceptions. You just can’t write a single line of code without making ~5 catch clauses around it, and that basically kills my creativity.
      However, I think that documenting code thoroughly is a must in any software project, thus exception specifications can be a very good thing in that they provide this extra documentation (which is even enforced).

      1. Actually, the Java enforcement is quite OK.

        The problem isn’t the enforcement; it’s the proliferation of exceptions. Exceptions are meant to be just that: exceptions. They’re not meant to be just another way to handle errors.

        If people followed this rule, exception handling would be easy.

        1. I agree!

          Just a little note though; Errors are an exception in a way. Errors happen when there’s something not quite right – i.e, when there’s an exception. :)

          1. Technically yes. But there’s a subtle difference. Errors are usually unrecoverable, or require drastic action to fix.

            Java, correctly, makes the distinction between these and other situations that are less problematic.

            Generally I see many instances where errors have been treated by throwing exceptions. Essentially the programmer is being lazy and saying ‘yeah… I can’t be bothered to deal with this cleanly, so I’ll pass the buck.’ These don’t need exceptions; they need proper error handling!

    1. That’s true. I have linked Boost’s recommendation against using expcetion specifications in my first comment on this thread as well.

      Thanks for posting.

Leave a Reply

Your email address will not be published. Required fields are marked *