Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Function exceptions specification and standard exceptions - foo() throw(Exception)

In C++ you may declare function with exception specification like this:

int foo() const throw(Exception);

I found those two links:

  • http://www.cplusplus.com/doc/tutorial/exceptions/ and
  • http://publib.boulder.ibm.com/infocenter/lnxpcomp/v8v101/index.jsp?topic=%2Fcom.ibm.xlcpp8l.doc%2Flanguage%2Fref%2Fcplr156.htm

But several things end up unanswered...

Question 1: why to add exception specification? Will it bring any performance increase? What will be different for compiler? Because it seems just like an information for programmer to me.

Question 2: what will happend (what should happen) if I throw something that isn't in specification? For example:

int foo() throw(int) {
        throw char; // Totally unrelated classes, not types in real
}

Question 3: function/method shouldn't throw anything. I found at least two (three, alternative syntax for different compilers) ways to specify no exception throwing:

  • int foo() throw();
  • int foo() __attribute(nothrow)__ for gcc
  • int foo() nothrow for visual C++

Which one is "correct"? Is there any difference? Which one should I use?

Question 4: "standart exceptions", bad_alloc,bad_cast,bad_exception,bad_typeid and ios_base::failure.

Ok bad_alloc is self explaining and I know how (and more importantly when) to use it (add to exception specification), but what about the others? None of them does really ring a bell... Which "code pieces" are they associated with? Like bad_alloc is associated with new char[500000].

Question 5: If I have exception classes hierarchy, like this:

    class ExceptionFileType {
             virtual const char * getError() const = 0;
    };

    class ExceptionFileTypeMissing : public ExceptionFileType {
            virtual const char *getError() cosnt {
                    return "Missing file";
            }
    }

Should I use:

    int foo() throw(ExceptionFileType);

Or:

    int foo() throw(ExceptionFileTypeMissing,ExceptionFileTypeNotWritable,ExceptionFileTypeNotReadable,...)

Note: answers with references would be great. I'm looking for good practice tips.

like image 650
Vyktor Avatar asked Dec 16 '22 03:12

Vyktor


2 Answers

The simple "good practice" tip is: don't use exception specifications.

Essentially the only exception to that is the possibility of an empty exception specification: throw(). That's sufficiently useful that in C++11 it's been given its own keyword (noexcept). It's generally agreed that any non-empty exception specification is a lousy idea though.

Exception specifications (other than noexcept) are officially deprecated -- and unlike many deprecated features, removing this would affect little enough source code that I think there's a good chance it really will eventually be removed (certainly no guarantee, but a pretty fair chance anyway).

As for what happens when/if you do throw an exception of a type not allowed by the exception specification: std::unexpected() gets invoked. By default, that invokes terminate(). You can use std::set_unexpected to set your own handler -- but about all you can reasonably do is add some logging before you terminate(). Your unexpected handler is not allowed to return.

like image 72
Jerry Coffin Avatar answered May 03 '23 03:05

Jerry Coffin


Question 1

Don't bother. They were a bad idea, and were deprecated in the latest version of the language. They give no benefit to the compiler, since they are checked at runtime; if anything, they might hurt performance in some cases.

Question 2

A function called std::unexpected will be called. By default, this calls std::terminate; by default, that terminates the program. Both of these behaviours can be changed, using std::set_unexpected and std::set_terminate to install your own handler, if you really want to.

Question 3

throw() was the standard way to do it; the others are non-portable compiler extensions. In C++11, you might use noexcept, which gives a compile-time check that nothing can throw, rather than a run-time check that nothing does throw.

Question 4

  • bad_cast is thrown when a dynamic_cast of a reference fails.
  • bad_exception is thrown in some weird circumstances when an exception specification is violated.
  • bad_typeid is thrown if evaluating the argument to typeid involves dereferencing a null pointer
  • ios_base::failure is thrown by the input/output library (<iostream> etc.) when some operations fail

Question 5

If you want to allow the entire heirarchy to be thrown, then just specify the base class. But you shouldn't be using exception specifiers at all.

like image 31
Mike Seymour Avatar answered May 03 '23 03:05

Mike Seymour