______________________________________________________________________

  15   Exception handling                             [except]

  ______________________________________________________________________

1 Exception handling provides a way of transferring control and informa­
  tion  from  a point in the execution of a program to an exception han­
  dler associated with a point previously passed by  the  execution.   A
  handler  will  be  invoked  only by a throw-expression invoked in code
  executed in the handler's try-block or in functions  called  from  the
  handler's try-block.
          try-block:
                   try compound-statement handler-seq
          handler-seq:
                  handler handler-seqopt
          handler:
                  catch ( exception-declaration ) compound-statement
          exception-declaration:
                  type-specifier-seq declarator
                  type-specifier-seq abstract-declarator
                  type-specifier-seq
                  ...
          throw-expression:
                  throw assignment-expressionopt
  A  try-block  is  a statement (_stmt.stmt_).  A throw-expression is of
  type void.  A throw-expression is sometimes referred to  as  a  throw-
  point.   Code  that  executes  a  throw-expression is said to throw an
  exception; code that subsequently gets control is called a handler.

2 A goto, break, return, or continue statement can be used  to  transfer
  control  out  of  a try-block or handler, but not into one.  When this
  happens, each variable declared in the try-block will be destroyed  in
  the context that directly contains its declaration.  For example,
          lab:  try {
                     T1 t1;
                     try {
                            T2 t2;
                            if (condition)
                                  goto lab;
                     } catch(...) { /* handler 2 */ }
                } catch(...) { /*  handler 1 */ }
  Here,  executing goto lab; will destroy first t2, then t1.  Any excep­
  tion raised while destroying t2 will result in  executing  handler  2;
  any exception raised while destroying t1 will result in executing han­
  dler 1.

  15.1  Throwing an exception                             [except.throw]

1 Throwing an exception transfers control to a handler.   An  object  is
  passed and the type of that object determines which handlers can catch
  it.  For example,
          throw "Help!";
  can be caught by a handler of some char* type:
          try {
              // ...
          }
          catch(const char* p) {
              // handle character string exceptions here
          }
  and
          class Overflow {
              // ...
          public:
              Overflow(char,double,double);
          };
          void f(double x)
          {
              // ...
              throw Overflow('+',x,3.45e107);
          }
  can be caught by a handler
          try {
              // ...
              f(1.2);
              // ...
          }
          catch(Overflow& oo) {
              // handle exceptions of type Overflow here
          }

2 When an exception is thrown, control is  transferred  to  the  nearest
  handler with an appropriate type; nearest means the handler whose try-
  block was most recently entered by the thread of control and  not  yet
  exited; appropriate type is defined in _except.handle_.

3 The  operand  of  a  throw  shall  be of a type with no ambiguous base
  classes.  That is, it shall be possible to convert  the  value  thrown
  unambiguously to each of its base classes.1)

4 A throw-expression initializes a temporary object of the  static  type
  of  the  operand  of  throw  and uses that temporary to initialize the
  appropriately-typed variable named in the handler.  If the static type
  of  the  expression  thrown  is a class or a pointer or reference to a
  class, there shall be an unambiguous conversion from that  class  type
  to  each  of its accessible base classes.  Except for that restriction
  and forthe restrictions on type matching mentioned in  _except.handle_
  _________________________
  1)  If  the  value thrown has no base classes or is not of class type,
  this condition is vacuously satisfied.

  and  the  use of a temporary variable, the operand of throw is treated
  exactly as a function argument in a call (_expr.call_) or the  operand
  of a return statement.

5 The  memory  for  the  temporary copy of the exception being thrown is
  allocated in an implementation-defined way.  The temporary persists as
  long as there is a handler being executed for that exception.  In par­
  ticular, if a handler exits by  executing  a  throw;  statement,  that
  passes  control to another handler for the same exception, so the tem­
  porary remains.  If the use of the temporary object can be  eliminated
  without  changing  the meaning of the program except for the execution
  of constructors and destructors associated with the use of the  tempo­
  rary object (_class.temporary_), then the exception in the handler may
  be initialized directly with the argument of the throw expression.

6 A throw-expression with no operand rethrows the exception  being  han­
  dled  without  copying  it.   For  example, code that must be executed
  because of an exception yet cannot completely handle the exception can
  be written like this:
          try {
              // ...
          }
          catch (...) {  // catch all exceptions

              // respond (partially) to exception

              throw;     // pass the exception to some
                         // other handler
          }

7 The exception thrown is the one most recently caught and not finished.
  An exception is considered caught when initialization is complete  for
  the formal parameter of the corresponding catch clause, or when termi­
  nate() or unexpected() is entered due to a  throw.   An  exception  is
  considered finished when the corresponding catch clause exits.

8 If  no  exception  is  presently  being  handled,  executing  a throw-
  expression with no operand calls terminate() (_except.terminate_).

  15.2  Constructors and destructors                       [except.ctor]

1 As control passes from a throw-point to  a  handler,  destructors  are
  invoked  for all automatic objects constructed since the try-block was
  entered.

2 An object that is partially constructed will have destructors executed
  only  for its fully constructed sub-objects.  Should a constructor for
  an element of an automatic array throw an  exception,  only  the  con­
  structed  elements  of that array will be destroyed.  If the object or
  array was allocated in a new-expression, the storage occupied by  that
  object is sometimes deleted also (_expr.new_).

3 The  process  of calling destructors for automatic objects constructed
  on the path from a try-block to a  throw-expression  is  called  stack

  unwinding.

  15.3  Handling an exception                            [except.handle]

1 The exception-declaration in a handler describes the type(s) of excep­
  tions that can cause that handler  to  be  executed.   The  exception-
  declaration shall not denote an incomplete type.

2 A  handler  with  type  T,  const  T, T&, or const T& is a match for a
  throw-expression with an object of type E if

    [1]T and E are the same type, or

    [2]T is an accessible (_conv.ptr_) base class  of  E  at  the  throw
      point, or

    [3]T is a pointer type and E is a pointer type that can be converted
      to T by a standard pointer conversion (_conv.ptr_)  at  the  throw
      point.

3 For example,
          class Matherr { /* ... */ virtual vf(); };
          class Overflow: public Matherr { /* ... */ };
          class Underflow: public Matherr { /* ... */ };
          class Zerodivide: public Matherr { /* ... */ };
          void f()
          {
              try {
                  g();
              }
              catch (Overflow oo) {
                  // ...
              }
              catch (Matherr mm) {
                  // ...
              }
          }
  Here,  the Overflow handler will catch exceptions of type Overflow and
  the Matherr handler will catch exceptions  of  type  Matherr  and  all
  types  publicly  derived  from Matherr including Underflow and Zerodi­
  vide.

4 The handlers for a try-block are tried in order of  appearance.   That
  makes  it  possible  to write handlers that can never be executed, for
  example by placing a handler for a derived class after a handler for a
  corresponding base class.

5 A ...  in a handler's exception-declaration functions similarly to ...
  in a function parameter declaration; it  specifies  a  match  for  any
  exception.   If  present,  a ...  handler must be the last handler for
  its try-block.

6 If no match is found among the handlers for a  try-block,  the  search
  for  a  matching  handler  continues in a dynamically surrounding try-

  block.  If no matching handler is found in  a  program,  the  function
  terminate() (_except.terminate_) is called.

7 An exception is considered handled upon entry to a handler.  The stack
  will have been unwound at that point.

  15.4  Exception specifications                           [except.spec]

1 A function  declaration  lists  exceptions  that  its  function  might
  directly  or indirectly throw by using an exception-specification as a
  suffix of its declarator.

  +-------                 BEGIN BOX 1                -------+
  Should it be possible to use  more  general  types  than  type-ids  in
  exception-specifications?
  +-------                  END BOX 1                 -------+

          exception-specification:
                  throw ( type-id-listopt )
          type-id-list:
                  type-id
                  type-id-list ,  type-id
  An  exception-specification shall appear only in a context that causes
  it to apply directly to a declaration or definition of a  function  or
  member function.  For example:
          extern void f() throw(int);           // OK
          extern void (*fp) throw (int);        // ill-formed
          extern void g(void f() throw(int));   // ill-formed
  If  any  declaration of a function has an exception-specification, all
  declarations, including the definition, of that function shall have an
  exception-specification  with  the same set of type-ids.  If a virtual
  function has an exception-specification, all  declarations,  including
  the  definition,  of any function that overrides that virtual function
  in any derived class must have an exception-specification at least  as
  restrictive as that in the base class.  For example:
          struct B {
              virtual void f() throw (int, double);
              virtual void g();
          };

          struct D: B {
              void f();                    // ill-formed
              void g() throw (int);        // OK
          };
  The  declaration  of  D::f  is ill-formed because it allows all excep­
  tions, whereas B::f allows only int and double.

2 Types may not be defined in exception-specifications.

3 An exception-specification can include the same class more  than  once
  and  can  include classes related by inheritance, even though doing so
  is redundant.  An exception-specification  can  include  classes  with
  ambiguous  base  classes, even though throwing objects of such classes
  is ill-formed (_except.throw_).  An exception specification  can  also

  include identifiers that represent incomplete types.2)

4 If a class X is in the type-id-list of the exception-specification  of
  a  function, that function is said to allow exception objects of class
  X or any class publicly derived from X.  Similarly, if a pointer  type
  Y*  is  in  the type-id-list of the exception-specification of a func­
  tion, the function allows exceptions of type Y* or that  are  pointers
  to any type publicly derived from Y*.

  +-------                 BEGIN BOX 2                -------+
  This still needs to deal with const and volatile
  +-------                  END BOX 2                 -------+

  Whenever  an  exception  is  thrown  and  the  search  for  a  handler
  (_except.handle_) encounters the outermost block of a function with an
  exception-specification,   the   function   unexpected()   is   called
  (_except.unexpected_) if the exception-specification  does  not  allow
  the exception.  For example,
          class X { };
          class Y { };
          class Z: public X { };
          class W { };

          void f() throw (X, Y)
          {
              int n = 0;
              if (n) throw X();        // OK
              if (n) throw Z();        // also OK
              throw W();               // will call unexpected()
          }

5 An  implementation  shall not reject an expression merely because when
  executed it throws or might throw an  exception  that  the  containing
  function does not allow.  For example,
          extern void f() throw(X, Y);

          void g() throw(X)
          {
                  f();                 // OK
          }
  the  call  to  f is well-formed even though when called, f might throw
  exception Y that g does not allow.

6 A function with no exception-specification allows all  exceptions.   A
  function  with  an  empty  exception-specification,  throw(), does not
  allow any exceptions.

  _________________________
  2)  This makes sense, for example, in declaring a function that is de­
  fined elsewhere.  It probably does not make sense in a function  defi­
  nition,  because  the type would have to be completed before an object
  of that type could be constructed and thrown.

7 An exception-specification is not  considered  part  of  a  function's
  type.

  15.5  Special functions                               [except.special]

1 The  exception handling mechanism relies on two functions, terminate()
  and unexpected(), for coping with errors related to the exception han­
  dling  mechanism  itself.  These functions are declared in <exception>
  and <exception.ns> (_lib.header.exception_).

  15.5.1  The terminate() function                    [except.terminate]

1 Occasionally, exception handling must be  abandoned  for  less  subtle
  error handling techniques.  For example,

  --when  a exception handling mechanism, after completing evaluation of
    the object to be thrown, calls a user function  that  exits  via  an
    uncaught exception,3)

  --when  the  exception  handling mechanism cannot find a handler for a
    thrown exception,

  --when the exception handling mechanism finds the stack corrupted, or

  --when a destructor called during stack unwinding caused by an  excep­
    tion tries to exit using an exception.

2 In such cases,
          void terminate();
  is  called;  terminate()  calls  the function given on the most recent
  call of set_terminate():
          typedef void(*PFV)();
          PFV set_terminate(PFV);

3 The previous function given to  set_terminate()  will  be  the  return
  value; this enables users to implement a stack strategy for using ter­
  minate().  The default function called by terminate() is abort().

4 The function given as argument to set_terminate, if called, shall  not
  return to its caller.  It should either terminate execution by explic­
  itly calling exit() or abort() or loop infinitely.  The effect of such
  a  function  trying  to  return  to  its caller, either by executing a
  return statement or throwing an exception, is undefined.

  15.5.2  The unexpected() function                  [except.unexpected]

1 If a function with an exception-specification throws an exception that
  is not listed in the exception-specification, the function
          void unexpected();
  _________________________
  3)  For  example, if the object being thrown is of a class with a copy
  constructor, terminate() will be called if that copy constructor exits
  with an exception during a throw.

  is  called;  unexpected()  calls the function given on the most recent
  call of set_unexpected():
          typedef void(*PFV)();
          PFV set_unexpected(PFV);
  The previous function given to set_unexpected()  will  be  the  return
  value;  this  enables  users  to  implement a stack strategy for using
  unexpected().  The default function called by unexpected()  is  termi­
  nate().   Since the default function called by terminate() is abort(),
  this leads to immediate and precise detection of the error.

2 The unexpected() function shall not return, but it can throw  (or  re-
  throw)  an  exception.  Handlers for this exception will be looked for
  starting at the call of the function whose exception-specification was
  violated.   Thus  an  exception-specification  does not guarantee that
  only the listed classes will be thrown.  For example,
          void  pass_through() { throw; }
          void  f(PFV pf) throw()   // f claims to throw no exceptions
          {
                  (*pf)();          // but the argument function might
          }
          void  g(PFV pf)
          {
                  set_unexpected(&pass_through);
                  f(pf);
          }
  After the call in g() to set_unexpected(), f() behaves as if it had no
  exception-specification at all.

  15.6  Exceptions and access                            [except.access]

1 The  parameter  of  a  catch  clause  obeys the same access rules as a
  parameter of the function in which the catch clause occurs.

2 An object may be thrown if it can be copied and destroyed in the  con­
  text of the function in which the throw occurs.