Topic: Exceptions and dtors


Author: charleyb@gr.hp.com (Charley Bay (Contract))
Date: 1995/12/05
Raw View
Dick Menninger (Dick.Menninger@daytonoh.attgis.com) wrote:
: You only catch and fully process those that are appropriate.
: It can clear an exception if it handles that exception.  [snip]
: The catch code could also decide that some catch code further out
: should handle the rest of an exception , possibly after doing some
: partial processing.

Ok, I like this.  I feel much better.  So, would the following
exception stack be possible under this proposal?

   Exception Stack (ALL EXCEPTIONS GENERATED AT CALLING LEVEL 4+).
   (int)exception            --must be processed at calling level 1.
   (Dog&)exception           --must be processed at calling level 3.
   (double)exception         --must be processed at calling level 1.
   (PotatoPeeler&)exception  --must be processed at calling level 2.

If so, then I absolutely agree:  This would undeniably clean
up a lot of exception handling code.  (Multiple simultaneous
exceptions would be a very good thing that I would actively use.)

: There are two situations here, at least.  The order of exceptions
: does not imply importance or relatedness at all.  That is one of
: the key issues why you need a mechanism that allows multiple
: exceptions.

How do we gauge priority, then?  I assumed order of processing must
be the reverse of order generation, and that the last exception
generated must be serviced before the previous exception (after
all, it was an exception in the servicing process for a previous
exception).  I was assuming this was a stack frame limitation.

: The second issue is that you may need to see the set
: of exceptions (or subsets of them) at the same time in order
: to make the right decisions.

Wow.  That would certainly be impressive, and possibly very useful.
However, don't they still need to be serviced in reverse order from
generation?  Don't we have implied relatedness within a stack of
"int", "Banana", "int" exceptions?  I would think we must order
the two "int" exceptions somehow.

: [snip]
: handling errors has a major impact on design and code.  A good
: exception mechanism REDUCES the impact on (distortions of)
: the other parts of the design and code.

Agreed.

: A lot of that "buggy" code out there is really code that
: ignores proper error handling.

Aw, we both know that's because code with exception handling
is twice as much work as code that ignores problems.  :-)  :-)

: I suspect that with the schedule pressures of the
: standardization effort, only a limited improvement
: is practical.  But something that allows multiple
: exceptions to be muddled through, and so, allows
: the use of stuff (including other peoples' stuff)
: that might throw an exception while doing what really
: needs to be done, is needed to make this tollerable
: and to get experience to guide the next round.
: I see that as fixing something that is broke.

I must agree with this statement.  I do admit that
an execution of "void unexpected(void)" is a rather in-elegant
solution to a situation that will obviously occur quite
frequently.

When reading the standard, I was continuously presented with
the ancillary (implied) goal of "no limits".  Handling multiple
exceptions seems consistent with that, and I do admit it could
be tremendously advantageous.

I'm still trying to figure out how a "catch" block is going
attempt to handle three exceptions simultaneously, though...
Would it simply be implied that the catch(...) would iterate
once for each exception?  If so, what if it decides to re-throw
the exception to a higher level?  Does it come back for the
other two exceptions?  The compiler venders are going to
hate us.  :-)  :-)  :-)

--
--charley                              #include <stdisclaimer.h>
charleyb@gr.hp.com    -or-    charley@agrostis.nrel.colostate.edu




---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]





Author: schuenem@informatik.tu-muenchen.de (Ulf Schuenemann)
Date: 1995/11/30
Raw View

In article <48u966$ak9@steel.interlog.com>, herbs@interlog.com (Herb Sutter) writes:
[..]
|> Basically all that's now needed is a rule for when to stop.  There seem to be

Yes, having multiple exceptions is a usefull idea - the handling-rule
is the problem that has to be solved.
Just 2 more possibilities for brain-storming discussion:

|> two main possibilities.  In both, we keep unwinding (and possibly adding new
|> exceptions as we go) until a handler for any one of the active exceptions is
|> found [...]
c) One could also use a stack of exceptions: The last thrown exception
   has to be handled next. After handling it gets poped and the handler
   for the top-exception is searched until the stack of exceptions is empty.

|> Then, either:
|>
|> a) discard all other active exceptions and continue execution from the handler
|> for the first matched exception [...]
|>
|> b) invoke the matching handler, remove the handled exception from the list,
|> then continue unwinding [...]

d) One handler could handle a set of exceptions - after the handler is done
   all exceptions it could have handled are removed from the set of
   to-be-handled exceptions.
   Ie given struct A {...}; struct B {...}; struct C : A, B {...};
   a handler for C handles all A, B and C exceptions.


Ulf Schuenemann

--------------------------------------------------------------------
 ,_.   Ulf Schuenemann
#,  \  Fakultaet fuer Informatik, Technische Universitaet Muenchen, Germany.
 |   > email: schuenem@informatik.tu-muenchen.de
 v=-<  WWW:   http://hphalle2.informatik.tu-muenchen.de/~schuenem/

---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]





Author: John Max Skaller <maxtal@suphys.physics.su.oz.au>
Date: 1995/12/01
Raw View
Dick Menninger <Dick.Menninger@daytonoh.attgis.com> wrote:
>
>> ==========Herb Sutter, 11/21/95==========
>
>> PROBLEM: Basic Inconsistency (Ctors and Dtors and Exceptions)

> Any code, including code
>called in ctors, dtors, and exception handlers or implied
>execution in the companion unwinding that must occur
>should be capable of being common use code that can
>want to throw exceptions independent of any other
>exception already thrown.

  Thats a reasonable axiom, IMHO.

  The problem is: how to handle multiple exceptions?

  Here's a proposition: the stack continues to be unwound
  until ALL exceptions are handled. Reason: after this is the case,
  the system is in a "normal" state.

  Agree?

  So now, the question becomes, HOW to handle multiple
  exceptions.

  The current idiom is that a SINGLE exception is handled
  by unwinding to the last appropriate handler pushed
  onto the stack.

> From an application design
>viewpoint, THIS IS NOT A RACE with the first exception
>thrown winning and the rest swallowing their "pride"
>and going quietly away.


  Agree.

>Exceptions provide a control paradigm in for readily
>backing out of portions of call stacks that allows
>some disciplined use of call frames for data.  But
>the current scheme seriously limits it useability.

  Yes. But such limited useablility is better than
manually passing error codes around.

  Let me say that again another way: the existing
paradigm is sufficient for bimodal processing
state in which "backing out" is exception free
and other processing can throw exceptions.

  This is enough for destructors which simply
release memory (in lieu of garbage collection).

  It's NOT enough for "active" destruction though,
such as "construction as resource acquisition,
destruction is resource release" where releasing
a resource can lead to errors.

  For example destroying a file object may
attempt to flush buffers to disk, if the disk
is down, one exception thrown can trigger a chain
reaction.

  This suggests that USERS have to build appropriate
data structures if the simple stack model isn't enough.
For example, testing flags and scheduling a recovery
task --- well outside the scope of a simple "back out"
model.

  Since I suspect requirments will vary greatly
here it's hard to see how to standardise this
in the language. Any suggestions?

>> PROPOSAL: Allow Multiple Simultaneous Exceptions

  MEH. Multiple Exception Handling.

>First the possible multiple exceptional
>condition cases need to be more accurately delineated.

>Then the handler cases need to be matched up with them.

>First, there may be unrelated exceptional conditions that
>occur in the same window (window meaning
>additional conditions need to be detected and raised
>before some other condition has been handled).

>Second, there may be similar but distinct exceptional
>conditions that occur in the same window.  Third,
>there may be separate detections of a common
>exceptional condition.  Each of these may have its
>own needs.

>In the third case, it may be much easier to write
>software if you could say something to the effect of
>"throw this if it is not already thrown."  Obviously,
>determining the identity for "already thrown" is the
>key issue.

  Good point. It was suggested too.

>But here the needs are on the throw side.

  Yes.

>In the second case, the exceptions need to be
>distinguishable at throw time yet are likely to need
>handling by the same handler or sequence of handlers
>as you unfold.  Here, the handler may successfully
>handle one subset and need the throw a remaining
>subset.

  Right. But this ALSO can be handled by being able
to examine the SET of thrown exceptions -- in the handler
this time, rather than at the throw point.

  1. Write error(pointer_to_object) functions
     and call them, do NOT throw objects directly.

  2. In the error function, add the current exception to be
     thrown to the current exception LIST.

  3. IF the list is current ALREADY thrown, just return,
     stack unwinding just continues.

  4. Otherwise, throw the list (which contains one
     exception object).

  5. The handler catches the whole list. It processes some of
     the list elements, adds a few new ones, and if the list
     is not empty, rethrows it. otherwise it drops out,
     and we're back in normal processing mode.

  6. IF destruction must be stopped immediately in step 1,
     you have to test before you call the error function,
     OR check the return code and use a GOTO to skip over
     code still trying to establish new stack frames
     (Destructors can create objects too)

     This is messy but there CAN'T be a generalised automatic
     solution, for the simple reason only YOU know whether you
     should continue destruction or not.


     NESTED exceptions would GREATLY aid in simplifying this
     task, but you'd have to have a suitable handler
     close enough to catch such a nested exception.

     I believe this is the INTENT of the committee, even though
     the current words don't quite support nested exceptions.

     For example you should be able to throw an exception INSIDE
     the body of a destructor and catch it, pre-empting the
     execution of the rest of the body, but then continuing
     unwinding for the first exception which triggered destruction
     in the first place.

>Some may even fit part of the first case as
>discussed below.
>
>In the first case, you have two kinds of handler
>situations: single handlers that handle unrelated
>exceptional conditions and cascades of handlers
>that handle various subsets of them, both of these
>having the potential of successfully handling subsets
>and rethrowing other subsets.  The order of looking
>at the exceptional conditions may matter.  [cascade
>is used in the sense of multiple catches for a try.]


  See above: It is relatively easy to take over the complete
handling of exceptions, provided you manage ALL of them yourself.
It is not so easy if other components do not cooperate
with your scheme. It is also not so easy to decide
exactly HOW to handle all those exceptions !!

>These are the static cases.  That is, these are the
>cases where the appilcation exception handling
>is fixed at compile time.  I bring this up as a distinction
>as I also see a possible need for runtime exception
>handling binding.  However, compared to the static
>case this is a frill where a good solution for the static
>case is a necessity.

  You can already do dynamic handling,
you can use polymorphism, or even function pointers,
dynamic cast, or a variety of other techniques.

  For example, how about:


   catch( void (*xf)() ) { (xf) (); } // execute the exception !!

  Even more interesting:

  struct DynExp {
    virtual DynExp *handle()=0;
  }

  catch(DynExp *p) { DynExp *q = p->handle(); if(q) throw q; }

Here, the exception is executed and returns the next
exception to throw (if not 0).

IMHO: the problem is NOT how to implement a flexible EH scheme,
the problem is how to STANDARDISE it. There's too
many choices, and we don't know enough.

>I feel that much of the squirming going on now with
>respect to exceptions is because the current mechanism
>in woefully inadequate in fundamental ways.

  Actually, I think it is the opposite. It is not
inadequate, it is so powerful that the problem is
figuring out where to go next.

>An amazing
>amount of code never checks for errors since that
>would "distort" the design too much.  Taking a major
>part of that distortion away is what the exception
>control paradigm should do (making this excuse
>as lame as feasible), but it has to solve the full problem
>to be truly useable.

  I agree. But we actually have to solve it!

  I contend we have a very good start with the tools
available -- the problem is that the tools we have are
TOO flexible -- so that there's a problem with applications
using multiple libraries cooperating in the MEH
scheme.

  There are more basic problems, like implementing
"finally" requires some contortion: C++ has
grossly inadequate control structures: you can
emulate almost anything, but you have to emulate it.

  [Some things, like nested functions, can be emulated
   by a simple mechanical scheme, others, like threads,
   require writing what, in effect, would be a complete
   new language interpreter -- a task for which
   C++ is fairly well suited]

--
John Max Skaller               voice: 61-2-566-2189
81 Glebe Point Rd              fax:   61-2-660-0850
GLEBE NSW 2037                 email: maxtal@suphys.physics.oz.au
AUSTRALIA                      email: skaller@maxtal.com.au




---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]





Author: charleyb@gr.hp.com (Charley Bay (Contract))
Date: 1995/12/02
Raw View
: > c) One could also use a stack of exceptions: The last thrown exception
: >    has to be handled next.

: That would wind up being clumsy at best, and generally unworkable.

Interesting idea.  Just to remind myself, this is *only* so we
can throw exceptions in the destructor, right?

If we go this road, It sounds to me like all our "catch" blocks are
*really* going to have to be infinite loops, or we can't trust
that they really caught the problem we were looking for.  After
all, what's a catch for?  Catching the situation at the lowest level
where we know what's going on, but the highest level where we have
some authority to handle it.

We only catch because we think we can solve the problem.  If there
is a (potentially) infinite number of exceptions from each call,
then NO catch block can clear anything unless it keeps looking
for exceptions at that level (where it is better able to determine
context than any other point on the system):

Thus, every "catch" today is potentially using "the buck stops here"
approach.  To do that with an exception stack, every catch must be
in an infinite loop.

  try
  {
    foo();
  }
  while_catchable   // (Making up syntax)
  {                 // While we are still catching an infinite
    catch(int i)    //   number of exceptions from that stupid
    {               //   foo() call.
      ...do stuff.
    }
    catch(X& x)
    {
      ...do stuff.
    }
    catch(...)
    {
      ...do stuff.
    }
  }

My only issue with an exception loop is that arbitrarily catching
exceptions until there are no more completely hides what the heck
went wrong in the first place.  How to gauge the severity of the
first, second, third exception?  It will take some pretty serious
logic just to confirm what exception went first, second, third.
This level of complexity in MERELY exception processing is beyond
reasonable, IMHO.  The design should not require this level of
super-human exception processing effort.

--charley




---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]