Should I use an exception specifier in C++?

27,801

Solution 1

No.

Here are several examples why:

  1. Template code is impossible to write with exception specifications,

    template<class T>
    void f( T k )
    {
         T x( k );
         x.x();
    }
    

    The copies might throw, the parameter passing might throw, and x() might throw some unknown exception.

  2. Exception-specifications tend to prohibit extensibility.

    virtual void open() throw( FileNotFound );
    

    might evolve into

    virtual void open() throw( FileNotFound, SocketNotReady, InterprocessObjectNotImplemented, HardwareUnresponsive );
    

    You could really write that as

    throw( ... )
    

    The first is not extensible, the second is overambitious and the third is really what you mean, when you write virtual functions.

  3. Legacy code

    When you write code which relies on another library, you don't really know what it might do when something goes horribly wrong.

    int lib_f();
    
    void g() throw( k_too_small_exception )
    { 
       int k = lib_f();
       if( k < 0 ) throw k_too_small_exception();
    }
    

    g will terminate, when lib_f() throws. This is (in most cases) not what you really want. std::terminate() should never be called. It is always better to let the application crash with an unhandled exception, from which you can retrieve a stack-trace, than to silently/violently die.

  4. Write code that returns common errors and throws on exceptional occasions.

    Error e = open( "bla.txt" );
    if( e == FileNotFound )
        MessageUser( "File bla.txt not found" );
    if( e == AccessDenied )
        MessageUser( "Failed to open bla.txt, because we don't have read rights ..." );
    if( e != Success )
        MessageUser( "Failed due to some other error, error code = " + itoa( e ) );
    
    try
    {
       std::vector<TObj> k( 1000 );
       // ...
    }
    catch( const bad_alloc& b )
    { 
       MessageUser( "out of memory, exiting process" );
       throw;
    }
    

Nevertheless, when your library just throws your own exceptions, you can use exception specifications to state your intent.

Solution 2

Avoid exception specifications in C++. The reasons you give in your question are a pretty good start for why.

See Herb Sutter's "A Pragmatic Look at Exception Specifications".

Solution 3

I think the standardly except convention (for C++)
Exception specifiers were an experiment in the C++ standard that mostly failed.
The exception being that the no throw specifier is useful but you should also add the appropriate try catch block internally to make sure the code matches the specifier. Herb Sutter has a page on the subject. Gotch 82

In a addition I think it is worth describing Exception Guarantees.

These are basically documentation on how the state of an object is affected by exceptions escaping a method on that object. Unfortunately they are not enforced or otherwise mentioned by the compiler.
Boost and Exceptions

Exception Guarantees

No Guarantee:

There is no guarantee about the state of the object after an exception escapes a method
In these situations the object should no longer be used.

Basic Guarantee:

In nearly all situations this should be the minimum guarantee a method provides.
This guarantees the object's state is well defined and can still be consistently used.

Strong Guarantee: (aka Transactional Guarantee)

This guarantees that the method will complete successfully
Or an Exception will be thrown and the objects state will not change.

No Throw Guarantee:

The method guarantees that no exceptions are allowed to propagate out of the method.
All destructors should make this guarantee.
| N.B. If an exception escapes a destructor while an exception is already propagating
| the application will terminate

Solution 4

gcc will emit warnings when you violate exception specifications. What I do is to use macros to use the exception specifications only in a "lint" mode compile expressly for checking to make sure the exceptions agree with my documentation.

Solution 5

The only useful exception specifier is "throw()", as in "doesn't throw".

Share:
27,801
1800 INFORMATION
Author by

1800 INFORMATION

Multitudinis imperitæ non formido judicia; meis tamen, rogo, parcant opusculis——in quibus fuit propositi semper, a jocis ad seria, a seriis vicissim ad jocos transire.

Updated on May 26, 2020

Comments

  • 1800 INFORMATION
    1800 INFORMATION almost 4 years

    In C++, you can specify that a function may or may not throw an exception by using an exception specifier. For example:

    void foo() throw(); // guaranteed not to throw an exception
    void bar() throw(int); // may throw an exception of type int
    void baz() throw(...); // may throw an exception of some unspecified type
    

    I'm doubtful about actually using them because of the following:

    1. The compiler doesn't really enforce exception specifiers in any rigorous way, so the benefits are not great. Ideally, you would like to get a compile error.
    2. If a function violates an exception specifier, I think the standard behaviour is to terminate the program.
    3. In VS.Net, it treats throw(X) as throw(...), so adherence to the standard is not strong.

    Do you think exception specifiers should be used?
    Please answer with "yes" or "no" and provide some reasons to justify your answer.

  • Greg Rogers
    Greg Rogers over 15 years
    in 3, it would technically be std::unexpected not std::terminate. But when this function is called, the default invokes abort(). This generates a core dump. How is this worse than an unhandled exception? (which does basically the same thing)
  • paercebal
    paercebal over 15 years
    @Greg Rogers: An uncatched exception still does stack unwinding. This means destructors will be called. And in those destructors, a lot can be done, like: Resources correctly freed, logs correctly written, other processes will be told the current process is crashing, etc.. To summarize, it's RAII.
  • buti-oxa
    buti-oxa about 15 years
    Can you please add a reason why it is useful?
  • Matt Joiner
    Matt Joiner over 14 years
    why is it not useful? there's nothing more useful than knowing some function isn't going to start throwing exceptions left right and center.
  • Matt Joiner
    Matt Joiner over 14 years
    java programmers over the age of 30 should feel bad they couldn't cope with C, they have no excuse to be using java.
  • Harold Ekstrom
    Harold Ekstrom over 14 years
    Please see the Herb Sutter discussion referenced in Michael Burr's answer for detailed explanation.
  • David Thornley
    David Thornley about 14 years
    You left out: This effectively wraps everything in a try {...} catch (<specified exceptions>) { <do whatever> } catch (...) { unexpected(); ] construct, whether or not you want a try block there.
  • David Thornley
    David Thornley about 14 years
    The guarantees are something every C++ programmer must know, but they don't seem to me to be related to exception specifications.
  • Martin York
    Martin York about 14 years
    @David Thornley: I see the guarantees as what the exception specifications should have been (i.e. A method with the strong G can not call a method with a basic G without protection). Unfortunately I am not sure they are defined well enough to be enforced in a useful way be the compiler.
  • Logan Capaldo
    Logan Capaldo about 14 years
    @paercebal That is incorrect. It is implementation defined whether or not destructors are run for uncaught exceptions. Most environments don't unwind the stack/run destructors if the exception isn't caught. If you want to portably ensure your destructors run even when an exception is thrown and not handled (this is of dubious value), you need to write your code like try { <<...code...>> } catch(...) /* stack guaranteed to be unwound here and dtors run */ { throw; /* pass it on to the runtime */ }
  • Michael Burr
    Michael Burr about 12 years
    @awoodland: the use of 'dynamic-exception-specifications' (throw(optional-type-id-list)) is deprecated in C++11. They are still in the standard, but I guess a warning shot has been sent that their use should be considered carefully. C++11 adds the noexcept specification and operator. I don't know enough details about noexcept to comment on it. This article appears to be rather detailed: akrzemi1.wordpress.com/2011/06/10/using-noexcept And Dietmar Kühl has an article in the June 2011 Overload Journal: accu.org/var/uploads/journals/overload103.pdf
  • curiousguy
    curiousguy over 7 years
    "Here’s what many people think that exception specifications do: - Guarantee that functions will only throw listed exceptions (possibly none). - Enable compiler optimizations based on the knowledge that only listed exceptions (possibly none) will be thrown. The above expectations are, again, deceptively close to being correct." Actually, both are exactly right.
  • Martin York
    Martin York over 7 years
    @curiousguy. That's how Java does it because it checks are enforced at compile time. C++ are runtime checks. So: Guarantee that functions will only throw listed exceptions (possibly none). Not true. It only guarantees that if the function does throw these exceptions the application will terminate. Enable compiler optimizations based on the knowledge that only listed exceptions (possibly none) will be thrown Can't do that. Because I just pointed out you can't guarantee that the exception will not be thrown.
  • Martin York
    Martin York over 7 years
    @curiousguy: But as I said above (in 2008) exception specifications were an experiment that failed. And in C++11 exception specifications were deprecated in the language. Now we have noexcept to mark non-throwing methods (which act like const (you can't call a throwing method from a non-throwing method)).
  • curiousguy
    curiousguy over 7 years
    @LokiAstari "That's how Java does it because it checks are enforced at compile time_" Java exception spec is an experiment that failed. Java does not have the most useful exception spec: throw() (does not throw). "It only guarantees that if the function does throw these exceptions the application will terminate" No "not true" means true. There is no guarantee in C++ that a function does not call terminate(), ever. "Because I just pointed out you can't guarantee that the exception will not be thrown" You guarantee that the function will not throw. Which is exactly what you need.
  • curiousguy
    curiousguy over 7 years
    from cppreference.com: dynamic exception specification "Potential exceptions Each function f, pointer to function fp, and pointer to member function mfp has a set of potential exceptions, which consists of types that might be thrown. Set of all types indicates that any exception may be thrown. This set is defined as follows: 1) If the declaration of f, fp, or mfp uses throw()(deprecated) or noexcept, the set is empty."
  • Martin York
    Martin York over 7 years
    @curiousguy: N3690 is nearly two years out of date: The current version of the standard is: N4618 open-std.org/jtc1/sc22/wg21/docs/papers/2016/n4618.pdf or github.com/cplusplus/draft/blob/master/papers/n4618.pdf Here is a list of standard versions: stackoverflow.com/a/4653479/14065
  • Martin York
    Martin York over 7 years
    @curiousguy: With noexcept the result is still a call to terminate(). The subtle difference from previous is that it is implementation defined if the stack is unwound.
  • Martin York
    Martin York over 7 years
    @curiousguy: I did no understand any of your other comments. They made no sense to me.
  • curiousguy
    curiousguy over 7 years
    I was under the impression that your sentence "It only guarantees that if the function does throw these exceptions the application will terminate" was only applicable to a dynamic exception spec (throw (some,list)) not to nothrow. It's the reason why I mentioned nothrow in my previous comment (which I have now deleted).
  • curiousguy
    curiousguy over 7 years
  • curiousguy
    curiousguy over 7 years
    "It is always better to let the application crash with an unhandled exception, from which you can retrieve a stack-trace, than to silently/violently die." How can a "crash" be better than a clean call to terminate()? Why don't just just call abort()?
  • curiousguy
    curiousguy over 7 years
    @MichaelBurr Only throw(something) is considered useless and a bad idea. throw() is useful.
  • curiousguy
    curiousguy over 7 years
    "Here’s what many people think that exception specifications do: bullet Guarantee that functions will only throw listed exceptions (possibly none). bullet Enable compiler optimizations based on the knowledge that only listed exceptions (possibly none) will be thrown. The above expectations are, again, deceptively close to being correct" No, the above expectations are absolutely correct.