tags:

views:

2330

answers:

14

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.

+2  A: 

Generally I would not use exception specifiers. However, in cases where if any other exception were to come from the function in question that the program would definitively be unable to correct, then it can be useful. In all cases, make sure to document clearly what exceptions could be expected from that function.

Yes, the expected behavior of a non-specified exception being thrown from a function with exception specifiers is to call terminate().

I will also note that Scott Meyers addresses this subject in More Effective C++. His Effective C++ and More Effective C++ are highly recommended books.

Kris Kumler
+3  A: 

If you're writing code that will be used by people that would rather look at the function declaration than any comments around it, then a specification will tell them which exceptions they might want to catch.

Otherwise I don't find it particularly useful to use anything but throw() to indicate that it doesn't throw any exceptions.

Branan
+2  A: 

Yes, if you're into internal documentation. Or maybe writing a libary that others will use, so that they can tell what happens without consulting the documentation. Throwing or not throwing can be considered part of the API, almost like the return value.

I agree, they are not really useful for enforcing correctness Java style in the compiler, but it's better than nothing or haphazard comments.

+22  A: 

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

See Herb Sutter's discussions starting here and follow the links for more gory details.

edit: Sutter's "A Pragmatic Look at Exception Specifications" is a better link.

Michael Burr
+2  A: 

They can be useful for unit testing so that when writing tests you know what to expect the function to throw when it fails, but there is no enforcement surrounding them in the compiler. I think that they are extra code that is not necessary in C++. Which ever you choose all that you should be sure of is that you follow the same coding standard across the project and the team members so that your code remains readable.

Odd
+23  A: 

Here 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 be 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() throws( k_to_small_exception )
    { 
       int k = lib_f();
       if( k < 0 ) throw k_to_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, then 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( 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.

Christopher
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)
Greg Rogers
@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.
paercebal
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
@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 */ }`
Logan Capaldo
+5  A: 

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.

Jeremy
+5  A: 

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 bloke 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 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 completely 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

Martin York
The guarantees are something every C++ programmer must know, but they don't seem to me to be related to exception specifications.
David Thornley
@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.
Martin York
+6  A: 

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

Harold Ekstrom
Can you please add a reason why it is useful?
buti-oxa
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
Please see the Herb Sutter discussion referenced in Michael Burr's answer for detailed explanation.
Harold Ekstrom
+2  A: 

No. If you use them and an exception is thrown that you did not specify, either by your code or code called by your code, then the default behavior is to promptly terminate your program.

Also, I believe their use has been deprecated in current drafts of the C++0x standard.

Ferruccio
+3  A: 

Exception specifications are not wonderfully useful tools in C++. However, there /is/ a good use for them, if combined with std::unexpected.

What I do in some projects is code with exception specifications, and then call set_unexpected() with a function that will throw a special exception of my own design. This exception, upon construction, gets a backtrace (in a platform-specific manner) and is derived from std::bad_exception (to allow it to be propagated if desired). If it causes a terminate() call, as it usually does, the backtrace is printed by what() (as well as the original exception that caused it; not to hard to find that) and so I get information of where my contract was violated, such as what unexpected library exception was thrown.

If I do this, I never allow propagation of library exceptions (except std ones) and derive all my exceptions from std::exception. If a library decides to throw, I will catch and convert into my own hierarchy, allowing for me to always control the code. Templated functions that call dependent functions should avoid exception specifications for obvious reasons; but it's rare to have a templated function interface with library code anyway (and few libraries really use templates in a useful manner).

coppro
A: 

Exception specifications = rubbish, ask any Java developer over the age of 30

Greg Dean
java programmers over the age of 30 should feel bad they couldn't cope with C, they have no excuse to be using java.
Matt Joiner
A: 

From the article:

http://www.boost.org/community/exception_safety.html

“It is well known to be impossible to write an exception-safe generic container.” This claim is often heard with reference to an article by Tom Cargill [4] in which he explores the problem of exception-safety for a generic stack template. In his article, Cargill raises many useful questions, but unfortunately fails to present a solution to his problem.1 He concludes by suggesting that a solution may not be possible. Unfortunately, his article was read by many as “proof” of that speculation. Since it was published there have been many examples of exception-safe generic components, among them the C++ standard library containers.

And indeed I can think of ways to make template classes exception safe. Unless you don't have control over all the sub-classes then you may have a problem anyway. To do this one could create typedefs in your classes that define the exceptions thrown by various template classes. This think the problem is as always tacking it on afterwards as opposed to designing it in from the start, and I think it's this overhead that's the real hurdle.

Marius
A: 

A "throw()" specification allows the compiler to perform some optimisations when doing code flow analysis if it know that function will never throw an exception (or at least promises to never throw an exception). Larry Osterman talks about this briefly here:

http://blogs.msdn.com/larryosterman/archive/2006/03/22/558390.aspx

TheJuice