[Exceptional C++ Style] Item 13: A Pragmatic Look at Exception Specifications

Paul Grenyer paul at paulgrenyer.co.uk
Tue Dec 7 03:18:34 EST 2004


--- Forwarded on behalf of David Sullivan ----

This chapter discusses the use of exception specifications. It discusses 
when and how they are used. Since the last C++ standard, what have we learnt 
about the use of this feature following practical experience of it.

Exception Specification Usage

// Can throw anything.
int Func();

// Cannot throw anything.
int Gunc() throw();

// Can throw either type A or B.
int Hunc() throw(A, B);

An exception spscification tells the compiler to do a run-time check that 
guarantees that only exceptions of certain types will be throw. If the 
guarantee is violated, the function unexpected is called. The specification 
below gurantees that function f will emit only exceptions of type A or B.

int f() throw(A, B);

In this case, if an exception of type other than A or B is thrown, the 
function unexpected is called.

int f() throw(A, B) {
   throw;  // unexpected is called
}

The default unexpected function can overidden using the standard 
set_unexpected function.
void MyUnexpectedHandler { .... }
std:set_unexpected( &MyUnexpectedHandler );

The user defined unexpected function cannot return via a usual function 
return. It can

- either translate the exception to a form permissable by the exception 
specification, by throwing an exception from the unexpected handler that 
complies with the specification of the original thrown function.

- or call terminate which ends the program. (The terminate function can be 
replaced, but any replacement must end the program).

A Shadow Type System

Is an exception specification part of the function's type? The exception 
specification has been described as a "shadow type system". There are 
anomolies between C++'s type system and the use of exception specifications.
void f() throw(A, B);
typedef void(*PF)() throw (A, B); // syntax error
PF *pf = f;

But similar code works without the typedef-:
void f() throw(A, B);
void(*pf)() throw (A, B);   // valid
PF *pf = f;     // valid

C++ does not allow an exception specifcation on a typedef'd function.
The use of assignment to a pointer to a function is restricted to a 
specification that must include at least the original throw types.
void f() throw(A, B);
void(*pf)() throw (A, B, C);
pf = f;         // valid, pf's throw type list is less restrictive

Exception specifications also affect the typing of a virtual function when 
the function is overridden.
class C {
  virtual void f() throw(A, B);
  }

class D: C {
  void f();    // error
  }

Summarising, the type system is affected by the use of exception 
specifications and different rules may apply.

Misunderstandings

There is confusion about what exception specifications provide. Several C++ 
experts claim that C++ programmers believe that an exception specification 
guarantee enforces that only the types specified are thrown. This is not so. 
A broken promise results a throw to the unexpected function call, and that 
usually results in the program ending via the terminate function.

Once inside the unexpected function, there are two choices-:

- Throw an exception which is allowed by the specification. The unexpected 
function will normally br user-defined as the global unexpected function is 
very unlikely to accomadate this strategy. If an permissable exception is 
not thrown, the terminate function is run.

- Throw an exception which is still disallowed by the specification (such as 
bad_exception). If the specification allows a bad_exception, that is the 
exception that is propagated. If it does not, the terminate function again 
looms.

Summary of what Exception Specifications do

- Enforce (not guarantee) at run-time that functions will throw only listed 
exceptions (possibly none)
- Enable or prevent compiler optimisations based on having to check whether 
listed exceptions are indeed being thrown.

int Hunc() throw(A, B) {
   return Junc();
}

is translated by compiler to

int Hunc() {
   return Junc();
}
catch (A) {
   throw;
}
catch (B) {
   throw;
}
catch (...) {
  std::unexpected();
}

The compiler has to do more work to enforce at run-time that only an 
exception that has been specified is thrown. It cannot assume that only 
certain exceptions may thrown.

Impact of Exception Specifications

This chapter has shown use of exception specifications imposes a performance 
penalty.

Other factors which may affect run-time performance when using exception 
specifications are-:

- Some compilers will not inline a function with a performance 
specification.
- Some compilers will add the compiler generated try/catch blocks, even when 
the function cannot throw an exception. The use of exception specifications 
increases coupling. e.g. Removing a type from a base class virtual 
function's exception specification will break derived classes at a stroke.

Summary

- Use of exception specifications imposes a performance panalty.
- A run-time unexpected error may not be desirable if an unspecified 
exception is thrown.
- Exception specifications cannot be used with function templates anyway as 
it's difficult to determine what the parameterised types will throw.
- Do not use exception specifications.





More information about the Effective-cpp mailing list