Topic: try/catch in a handler?
Author: objfactory@aol.com (ObjFactory)
Date: 26 Oct 1994 23:48:03 -0400 Raw View
Can a try block be written in a handler? If so, what is the meaning of:
struct A {} a;
struct B {} b;
try {
throw(a);
}
catch(A) {
try {
throw(b);
}
catch(B) {}
throw(); // a or b?
}
I had once convinced myself that this was disallowed, but rereading the
ARM and the September draft, I don't see that it is. I just don't know
what it means.
Bob Foster
Object Factory
objfactory@aol.com
Author: objfactory@aol.com (ObjFactory)
Date: 29 Oct 1994 01:04:02 -0400 Raw View
In article <CyDHEu.2y9@cwi.nl>, olaf@cwi.nl (Olaf Weber) writes:
>I was hoping to seen answer from someone more knowledgeable, but
>since that hasn't appeared (yet?), here's my opinion.
>According to the ARM, an exception is considered handled upon entry of
>a `catch' clause, but current until that clause has been left, so that
it can be rethrown if necessary. So in your example we have:
> struct A { } a;
> struct B { } b;
> try {
> throw a;
> } catch (A) {
> // Current "exception type" is A,
> // `throw;' would re-throw `a'.
> try {
> throw b;
> } catch (B) {
> // Current "exception type" is B,
> // `throw;' would re-throw `b'.
> }
> // Current "exception type" is A again.
> throw; // Re-throws `a'.
}
>This seems the most natural manner of handling "nested" exceptions,
>because otherwise severe restrictions would have to be placed on the
>functions that can be called inside a catch clause.
Well, perhaps others will be more forthcoming if there is some discussion
of the issues.
The interpretation you suggest requires the implementation to maintain a
stack of thrown exceptions of potentially unbounded size. But storage for
these exceptions cannot be dynamically allocated, since the original
exception may very well have been caused by running out of heap memory.
The standard might attempt to wordsmith around this paradox, e.g., "an
implementation may set an upper bound", as it does for the single-level
storage it appears to contemplate today, but this puts additional stress
on writers of portable programs.
On the other hand, this practice could be disallowed. Similiar "severe
restrictions" are already present in things that may be done by functions
called by destructors.
I personally believe that the exact wording of 15.1(7) suggests that your
interpretation is correct and that a stack of thrown exceptions must be
maintained. But this requires reading between the lines. It seems like, at
least, the standard should be more explicit on an issue that may require
implementation-defined restrictions and potentially non-portable behavior.
Bob Foster
Object Factory
Author: objfactory@aol.com (ObjFactory)
Date: 29 Oct 1994 18:24:13 -0400 Raw View
In article <38tus4$9n6@tools.near.net>, barmar@nic.near.net (Barry
Margolin) writes:
>In article <38sl42$amd@newsbf01.news.aol.com> objfactory@aol.com
(ObjFactory) writes:
>>The interpretation you suggest requires the implementation to maintain a
>>stack of thrown exceptions of potentially unbounded size. But storage
for
>>these exceptions cannot be dynamically allocated, since the original
>>exception may very well have been caused by running out of heap memory.
>Why would this require dynamic allocation? Why not put the active
>exception in the stack frame of the handler block?
>If the original exception was caused by running out of *stack* memory,
that
>causes other exception-handling problems as well.
Using the stack frame of the handler block to hold the current exception
would be very clever, but the standard seems to prevent you from doing so.
According to 15.1, "memory for the temporary copy of an exception is
allocated in an implementation-dependent way" at throw expression time and
"the temporary persists as long as there is a handler being executed for
that exception." Further, a throw; "rethrows the exception being handled
without copying it."
Avoiding a dynamically allocated "stack" of temporaries requires copying
the exception to the program stack on entry to a handler (an extra copy in
the case of handlers with pointer or reference exception-declarations) and
having throw; initialize a temporary (again) from that local copy.
Perhaps some language lawyer can think of a way that 15.1 can be
interpreted so as to allow this behavior?
There are a few practical difficulties, too, e.g., the amount of stack
storage that would be needed in the handler is not known at compile time.
But the idea has some appeal.
Anyway, back to the original question of the thread. I was pretty
embarrassed to pick up C++ Report this month and look at the answer to
*last* month's C++ Puzzle, which was an example of a handler with a try
block. I'm going to assume that it is very unlikely that this example
slipped by the editors of that journal for two months unless the authors
of the standard believe it is a valid construction. So, unless someone
cares to contradict, that answers my original question. The posting by
Olaf Weber was right on the money, and my thanks.
As to implementation (it's amazing how removing doubt makes thought
constructive), it seems like the most practical thing to do would be to
preallocate a fixed amount of storage for exception temporaries - which
could be settable by the developer - and allocate from that storage
stackwise until it is exhaused. At which point, the implementation can try
to get more dynamic storage if this can be done in a safe way, or not. If
no possibility of further temporary storage exists, then I see no
alternative but to call terminate(). Allowing the developer to specify the
amount of preallocated storage would go a long way toward achieving
portability.
That's enough for me, unless some reader thinks I need further education.
I still wish the standard were a little more explicit about this matter;
if nothing else, by including the above example of yet another reason for
calling terminate().
Bob Foster
Object Factory
objfactory@aol.com
Author: barmar@nic.near.net (Barry Margolin)
Date: 29 Oct 1994 12:56:36 -0400 Raw View
In article <38sl42$amd@newsbf01.news.aol.com> objfactory@aol.com (ObjFactory) writes:
>The interpretation you suggest requires the implementation to maintain a
>stack of thrown exceptions of potentially unbounded size. But storage for
>these exceptions cannot be dynamically allocated, since the original
>exception may very well have been caused by running out of heap memory.
Why would this require dynamic allocation? Why not put the active
exception in the stack frame of the handler block?
If the original exception was caused by running out of *stack* memory, that
causes other exception-handling problems as well.
--
Barry Margolin
BBN Internet Services Corp.
barmar@near.net