Topic: Handling exceptions from implicitly-called functions


Author: Scott Meyers <smeyers@netcom.com>
Date: 1997/03/12
Raw View
I have a few questions about the context in which certain exceptions are
thrown and where they may be handled.

What is the context for an exception thrown during construction of a
function parameter or return value?  Consider:

    class Widget {
    public:
      Widget(int);
      Widget(const Widget&);
    };

    Widget makeAWidget(Widget w)      // makeAWidget takes and returns
    try { return 1; } catch(...) {}   // a Widget by value;  it also has
                                      // a function try block handling
                                      // every type of exception

  a) Suppose an exception is thrown during construction of the
     parameter w.  Is the exception handled by makeAWidget's function
     try block?

       Widget x(1);
       makeAWidget(x);    // if Widget::Widget(const Widget&)
                          // throws, does makeAWidget's try block
                          // handle it?

  b) Does the answer change if the exception is thrown during type
     conversion from the type of the actual parameter to the type
     of the formal?  For example:

       makeAWidget(1);    // 1 must be converted to Widget to make
                          // the call succeed;  if Widget::Widget(int)
                          // throws (prior to the call to Widget's copy
                          // constructor to copy the temporary over to
                          // w), does makeAWidget's try block handle
                          // the exception?

  c) Same as (a), but for makeAWidget's return value:

       Widget makeAWidget(Widget w)     // if an exception is thrown
       try { return w; } catch(...) {}  // during construction of the
                                        // return value from w, does
                                        // makeAWidget's function try
                                        // block handle it?

  d) Same as (b), but for makeAWidget's return value:

       Widget makeAWidget(Widget w)     // if an exception is thrown
       try { return 1; } catch(...) {}  // during construction of the
                                        // temporary Widget from 1 or
                                        // the copying of the temporary
                                        // to the return value, does
                                        // makeAWidget's function try
                                        // block handle it?

On a sort-of-related topic, can function try blocks of constructors and
destructors handle exceptions thrown during construction and
destruction, respectively, of member subobjects?  For example:

  class Widget {
  private:
    Thingie t;    // Thingie is just some random class

  public:
    Widget() try {} catch(...) {}    // if t's constructor throws, is it
                                     // handled by the Widget constructor's
                                     // function try block handler?

    ~Widget() try {} catch(...) {}   // if t's destructor throws, is it
                                     // handled by the Widget destructor's
                                     // function try block handler?

I know I've read somewhere that the answer to the last two questions is
yes, but tonight I read through section 15 ("Exception Handling") of
the 12/96 WP, and I can't find any wording to that effect.  In fact,
15.3/11 seems to suggest that destruction of subobjects has already run
to completion by the time a function try block's handler is entered:

  11 The fully constructed base classes and members of an object  shall  be
     destroyed  before  entering  the  handler of a function-try-block of a
     constructor or destructor for that object.

I'd really appreciate any clarification you can provide for this issue,
ideally along with pointers to relevant CD sections.

Thanks,

Scott

----------------------------------------------------------------------------
Scott Meyers, Ph.D.                  Voice: 503/638-6028
Software Development Consultant      Fax:   503/638-6614
3051 SW Turner Road                  Email: smeyers@netcom.com
West Linn, Oregon  97068             WWW:   http://www.teleport.com/~smeyers
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: fjh@murlibobo.cs.mu.OZ.AU (Fergus Henderson)
Date: 1997/03/12
Raw View
Scott Meyers <smeyers@netcom.com> writes:

>On a sort-of-related topic, can function try blocks of constructors and
>destructors handle exceptions thrown during construction and
>destruction, respectively, of member subobjects?

Yes.  However, admittedly the draft is not very clear about this.
The strongest evidence is 15.1[except.throw]/2, combined with
15[except]/3.  Here's the former:

 |   15.1  Throwing an exception                             [except.throw]
 |
 | 2 When  an  exception  is  thrown, control is transferred to the nearest
 |   handler with a matching type (_except.handle_);  "nearest"  means  the
 |   handler  whose  try  block  was most recently entered by the thread of
 |   control and not yet exited.

This sort of begs the question, what is the sequence followed by the
thread of control?  Does the thread of control enter the function-try-block
before executing the constructors, or does it execute the constructors
first?  The next quote helps us answer this question:

 |15   Exception handling                                       [except]
 [...]
 | 3 A  function-try-block  associates a handler-seq with the ctor-initial-
 |   izer, if present, and the function-body.  An exception  thrown  during
 |   the  execution  of the initializer expressions in the ctor-initializer
 |   or during the execution of the function-body transfers  control  to  a
 |   handler in a function-try-block in the same way as an exception thrown
 |   during the execution of a try-block transfers control  to  other  han-
 |   dlers.

Since the ctor-initializer must be executed before the constructors of
members or bases can be executed, the thread of control must enter the
function-try-block before those constructors start.  You can also
presume that the corresponding destructors are executed before the
thread of control leaves the function-try-block (see below).

I suppose that if one were trying to interpret the draft perversely,
one could imagine scenarios where the sequence followed by the thread
of control differs depending on whether or not there is a
ctor-initializer, or where the thread of control exits and re-enters a
function-try-block but I'm sure that isn't the intent.

>I know I've read somewhere that the answer to the last two questions is
>yes, but tonight I read through section 15 ("Exception Handling") of
>the 12/96 WP, and I can't find any wording to that effect.  In fact,
>15.3/11 seems to suggest that destruction of subobjects has already run
>to completion by the time a function try block's handler is entered:
>
>  11 The fully constructed base classes and members of an object  shall  be
>     destroyed  before  entering  the  handler of a function-try-block of a
>     constructor or destructor for that object.

The above quote is the evidence needed to show that the thread of
control doesn't leave the function-try-block before executing the
destructors of sub-objects, because it says that the latter must
be executed before the former.  It also implies that if a destructor
for a sub-object throws an exception, then the implementation must, in
the process of stack unwinding, call the destructors for the remaining
sub-objects before entering the function-try-block.

--
Fergus Henderson <fjh@cs.mu.oz.au>   |  "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh>   |  of excellence is a lethal habit"
PGP: finger fjh@128.250.37.3         |     -- the last words of T. S. Garp.
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]