Topic: Exceptions: Are they a Bad Thing?
Author: muzok@microsoft.com (Muzaffer Kal)
Date: Wed, 26 Jan 1994 21:43:34 GMT Raw View
In article <759084735snz@vsfl.demon.co.uk> clint@vsfl.demon.co.uk wrote:
> ...
> This has already been discussed in depth. The answers to this
> concern have included using stack-allocated objects *EXCLUSIVELY*
> for all initilaisation and de-initialisation, so that the
> exception doesn't matter; and specifying exceptions raised in
> function signatures to make them more visible.
> ...
> --
> Ian Cameron Smith clint@vsfl.demon.co.uk
> Principal Software Engineer +44 (0)425 474484
> Virtual Software Factory LTD
>
> "Go ahead, punk, make my day"
If I understand the issue correctly what you can do is to wrap other
resource allocation into classes with constructors and destructors.
i.e. if you have a critical section which you enter at the beginning
of your function and you are afraid that an exception can cause premature
exit from your function without releasing the critical section, you can
create a class which enters the critical section in its constructors and
exists from the critical section in the destructor. This guarantees that
what ever path you take to exit from the function (including an exception)
your will release the critical section. This approach can be applied to
temporary heap allocations too. I believe "The C++ P.L." talks about this
under the title Resource Acquisition.
Muzaffer
The views expressed in this message are my own and
in no way reflect the views of Microsoft Corporation.
Author: richieb@bony1.bony.com (Richard Bielak)
Date: Mon, 24 Jan 1994 02:53:57 GMT Raw View
In article <2hj3qgE6ku@uni-erlangen.de> hmkirchh@cip.informatik.uni-erlangen.de (Hans-Joachim Kirchhoff) writes:
[...]
>No, no! As the developer of a library I don't always have the option of
>providing a default exception. In fact, I hardly ever have. When an error
>situation comes up, the library routine in most cases CANNOT deal with it.
>Throwing an exception ENSURES, that SOMEONE deals with it.
>
>Example: the collection class Array
>
>Data& Array::operator[](unsigned index)
>{
> if (index is out of range)
>// what do I do now? There is no default behaviour, since I HAVE to return
>// a valid reference to "Data".
> throw ArrayIndexOutOfRange (index);
>}
>
I agree with you that an exception is necessary in the code above, but
it does seem awkward. The problem is that "index-out-of-range" error
is caused by the client of ARRAY class so the exception should really
be generated in the client code.
This is the Bertrand Meyer's idea of "programming by contract". A
routine requires that certain things be true (i.e. preconditions)
before execution is attempted. In this case the preconditions is
that the index should be in range. If preconditions are met, then the
client has every right to expect that the postconditions of the
routine should be true, else the library routine gets an exception.
Here is how the same code would look in Eiffel:
item (index : INTEGER) : T is
require
(index >= min) and (index <= max)
do
...do whatever to retrieve the element from the array
end;
No exceptions need to be "thrown" by "item", because a client will get
an exception if the precondition is false.
--
* Richie Bielak (212)-815-3072 | *
* Internet: richieb@bony.com | "The best way to predict the future *
* Bang {uupsi,uunet}!bony1!richieb | is to invent it." *
* - Strictly my opinions - | - Alan C. Kay - *
Author: schmidt@net1.ics.uci.edu (Douglas C. Schmidt)
Date: 24 Jan 1994 17:41:22 -0800 Raw View
In article <CK45Dy.KEs@bony1.bony.com>,
Richard Bielak <richieb@bony1.bony.com> wrote:
++ Here is how the same code would look in Eiffel:
++
++ item (index : INTEGER) : T is
++ require
++ (index >= min) and (index <= max)
++ do
++ ...do whatever to retrieve the element from the array
++ end;
++
++ No exceptions need to be "thrown" by "item", because a client will get
++ an exception if the precondition is false.
This seems like a distinction without a difference. In both the C++
and Eiffel examples the client will get an exception and the logic the
client requires to recognize and handle this exception will also be
very similar (though I seem to recall that Eiffel uses a resumptive
exception handling model, unlike C++). Am I missing some
fundamentally different idea here?
Doug
--
Douglas C. Schmidt
Department of Information and Computer Science
University of California, Irvine
Irvine, CA 92717. Work #: (714) 856-4105; FAX #: (714) 856-4056
Author: da@bass.kidder.com (David Avraamides)
Date: Tue, 25 Jan 1994 00:17:42 GMT Raw View
In article <CK45Dy.KEs@bony1.bony.com> richieb@bony1.bony.com (Richard Bielak) writes:
[stuff deleted]
# >Example: the collection class Array
# >
# >Data& Array::operator[](unsigned index)
# >{
# > if (index is out of range)
# >// what do I do now? There is no default behaviour, since I HAVE to return
# >// a valid reference to "Data".
# > throw ArrayIndexOutOfRange (index);
# >}
# >
[stuff deleted]
# Here is how the same code would look in Eiffel:
#
# item (index : INTEGER) : T is
# require
# (index >= min) and (index <= max)
# do
# ...do whatever to retrieve the element from the array
# end;
#
# No exceptions need to be "thrown" by "item", because a client will get
# an exception if the precondition is false.
Disclaimer: I know nothing about Eiffel.
BUT, isn't the require clause doing exactly the same thing
that is in the C++ routine above? It seems the same to me
except the throw() is implicit in Eiffel and I don't know
how Eiffel indicates to the caller what type of "exception"
occurred. But in essence they seem quite similar...
--
David Avraamides
Kidder, Peabody & Co., Incorporated
da@kidder.com
Author: mbk%anl433.uucp@Germany.EU.net (Matt Kennel)
Date: 25 Jan 1994 23:11:30 GMT Raw View
David Avraamides (da@bass.kidder.com) wrote:
: In article <CK45Dy.KEs@bony1.bony.com> richieb@bony1.bony.com (Richard Bielak) writes:
: <example of exception handling in C++ vs preconditions in Eiffel for
bad inputs>
: BUT, isn't the require clause doing exactly the same thing
: that is in the C++ routine above? It seems the same to me
: except the throw() is implicit in Eiffel and I don't know
: how Eiffel indicates to the caller what type of "exception"
: occurred. But in essence they seem quite similar...
I believe the biggest difference is that the requirements imposed by
preconditions and postconditions are inherited by subtypes. Just as the
signature of routines has to be obeyed properly by subtypes as in most OO
languages, so must the user-defined conditions in Eiffel.
I think it's a good idea.
: --
: David Avraamides
--
-Matt Kennel mbk@inls1.ucsd.edu
-Institute for Nonlinear Science, University of California, San Diego
-*** AD: Archive for nonlinear dynamics papers & programs: FTP to
-*** lyapunov.ucsd.edu, username "anonymous".
Author: clint@vsfl.demon.co.uk (Ian Cameron Smith)
Date: Thu, 20 Jan 1994 16:52:15 +0000 Raw View
In article <2hjmplE49t@uni-erlangen.de> hmkirchh@cip.informatik.uni-erlangen.de writes:
> Several times now I saw the "old" return errorcode method being preferred
> to the new throw catch mechanism. However I cannot see any differences and
> certainly no advantages in writing
>
> int errorCode;
> errorCode=doSomething();
> if (errorCode!=0) {
> cleanup();
> return errorCode;
> }
>
> and
>
> try {
> doSomething();
> }
> catch () {
> cleanup();
> throw ;
> }
I think you may have missed some of the pro *and* against
points. See Dag Bruck's reply to your posting for one
good pro point.
My original concern was again something that comes up in a
bigger example. For instance, if function A calls B, and B
raises an exception that A catches, then what you have said is
true. However, if A calls B calls C calls D calls E calls F
calls G calls H calls I calls J calls K, and K raises an
exception that A catches, then consider the case of poor old F.
It doesn't throw an exception. It doesn't catch any. It may
even have been added-in to the call hierarchy, at a later date,
by someone who didn't know about the use of exceptions here.
But F can be terminated half-way through by an exception, with
*no* visible sign in the source code that this is so.
This has already been discussed in depth. The answers to this
concern have included using stack-allocated objects *EXCLUSIVELY*
for all initilaisation and de-initialisation, so that the
exception doesn't matter; and specifying exceptions raised in
function signatures to make them more visible.
PLEASE NOTE: This was my original worry. I'm not trying to
re-start the arguments about it here; my original point has
already been well-answered by many people, so let's not start
into this subject all over again. When the heat looks like
dying down, I'll post some kind of summary.
--
Ian Cameron Smith clint@vsfl.demon.co.uk
Principal Software Engineer +44 (0)425 474484
Virtual Software Factory LTD
"Go ahead, punk, make my day"
Author: pete@borland.com (Pete Becker)
Date: Fri, 21 Jan 1994 00:21:57 GMT Raw View
In article <2hlh2e$7ro@aludra.usc.edu>,
Eddy Kim <eddykim@aludra.usc.edu> wrote:
>
>Actually, with BC4.0, catch(...) causes a syntax error. catch( ... ) is
>accepted by the compiler. Anyone know if this is valid, a BC bug, or my
>copy of the compiler gone mad?
I just tried it with BC4 and it worked fine.
-- Pete
Author: hinton@netcom.com (Greg Hinton)
Date: Thu, 20 Jan 1994 23:45:56 GMT Raw View
In article <2hlh2e$7ro@aludra.usc.edu> eddykim@aludra.usc.edu (Eddy Kim) writes:
[stuff deleted]
>Actually, with BC4.0, catch(...) causes a syntax error. catch( ... ) is
>accepted by the compiler. Anyone know if this is valid, a BC bug, or my
>copy of the compiler gone mad?
My copy of BC++ 4.0 accepts catch(...) with no errors.
--
Greg Hinton
INET: hinton@netcom.com
UUCP: apple!netcom!hinton
Author: djones@megatest.com (Dave Jones)
Date: Wed, 19 Jan 1994 01:48:18 GMT Raw View
Exceptions: Are they a Bad Thing?
I think the short answer is, "Not always."
I do however think that generic classes should never throw exceptions. Stated
more strongly, THE EXCEPTION THROW AND CORRESPONDING EXCEPTION CATCH SHOULD
ALWAYS BE PLANNED AT THE SAME TIME. If you are writing a general purpose
routine, throwing exceptions puts unnecessary constraints on the user.
When I pull a library routine "off the shelf", I should be able to be
confident that it will not throw any exception unless I have told it that
I want it to do so.
Library code should provide default behavior that does not use exceptions.
Additionally, the user should be allowed to provide a fault-handler procedure
which is to be called before the function exits. If the user wants an exception
thrown, he can throw it in that procedure.
Author: hmkirchh@cip.informatik.uni-erlangen.de (Hans-Joachim Kirchhoff)
Date: Wed, 19 Jan 1994 10:59:28 GMT Raw View
djones@megatest.com (Dave Jones) writes:
>Library code should provide default behavior that does not use exceptions.
>Additionally, the user should be allowed to provide a fault-handler procedure
>which is to be called before the function exits. If the user wants an exception
>thrown, he can throw it in that procedure.
No, no! As the developer of a library I don't always have the option of
providing a default exception. In fact, I hardly ever have. When an error
situation comes up, the library routine in most cases CANNOT deal with it.
Throwing an exception ENSURES, that SOMEONE deals with it.
Example: the collection class Array
Data& Array::operator[](unsigned index)
{
if (index is out of range)
// what do I do now? There is no default behaviour, since I HAVE to return
// a valid reference to "Data".
throw ArrayIndexOutOfRange (index);
}
This way, no "IndexOutOfRange" error will get unnoticed.
I could of course simply terminate the program and print an error message
like "Ooops, so sorry, but "index out of range" etc..." but would you
as a user of the Array class like this "default" behavior? What if the
routine cannot print to stderr, because it runs in a grafical environment
like X or OS/2??
Hans-Jochen
---
-----------------------------------------------------------------------
Hans-Jochen Kirchhoff | hmkirchh@cip.informatik.uni-erlangen.de
| No signature today | CompuServe 100024,712 | Team OS/2
Fido 2:2490/2525
Author: vinoski@apollo.hp.com (Steve Vinoski)
Date: Wed, 19 Jan 1994 14:39:23 GMT Raw View
[Since this thread no longer has anything to do with the ANSI
standardization effort, follow-ups have been directed to
comp.lang.c++.]
In article <CJutBz.MH1@megatest.com> djones@megatest.com (Dave Jones) writes:
>Exceptions: Are they a Bad Thing?
>
>I think the short answer is, "Not always."
>
>I do however think that generic classes should never throw exceptions. Stated
>more strongly, THE EXCEPTION THROW AND CORRESPONDING EXCEPTION CATCH SHOULD
>ALWAYS BE PLANNED AT THE SAME TIME. If you are writing a general purpose
>routine, throwing exceptions puts unnecessary constraints on the user.
This is a poor argument. Please describe these "constraints" and
compare and constrast them to other error handling techniques.
>When I pull a library routine "off the shelf", I should be able to be
>confident that it will not throw any exception unless I have told it that
>I want it to do so.
Why? When I pull a library "off the shelf" I expect the designer to
have already completely considered how the services provided by the
library can fail, and to have made those failure modes an explicit
part of the library interface.
--steve
Steve Vinoski vinoski@apollo.hp.com (508)436-5904
Distributed Object Computing Program fax: (508)436-5122
Hewlett-Packard, Chelmsford, MA 01824
Author: hopps@yellow.mmm.com (Kevin J Hopps)
Date: Wed, 19 Jan 94 15:11:22 GMT Raw View
djones@megatest.com (Dave Jones) writes:
>Exceptions: Are they a Bad Thing?
>I think the short answer is, "Not always."
An even shorter answer is "No" :-)
>I do however think that generic classes should never throw exceptions. Stated
>more strongly, THE EXCEPTION THROW AND CORRESPONDING EXCEPTION CATCH SHOULD
>ALWAYS BE PLANNED AT THE SAME TIME. If you are writing a general purpose
>routine, throwing exceptions puts unnecessary constraints on the user.
>When I pull a library routine "off the shelf", I should be able to be
>confident that it will not throw any exception unless I have told it that
>I want it to do so.
I disagree. You might be able to win an argument for using return values
for reporting failures in functions which are capable of returning a value,
but the classic counter examples are still failures in constructors and
failures in operator functions. These are incapable of returning a status,
so some other mechanism must be used. Global variables create reentrancy
problems, and storing status in objects and testing them after every
operation is ugly to read and no fun to write. Also, few would dispute
the claim that many users of such classes lack the discipline to rigorously
test return values anyway. Like it or not, this lack of discipline is a
reality, and it leads to less reliable software.
Further, it is arguably impractical for authors of general purpose classes
to plan for how their code will be used, and to know whether users will
care *which* exceptions they throw (as opposed to just knowing *that*
exceptions are thrown).
>Library code should provide default behavior that does not use exceptions.
>Additionally, the user should be allowed to provide a fault-handler procedure
>which is to be called before the function exits. If the user wants an exception
>thrown, he can throw it in that procedure.
Writing code which calls fault handlers that may or may not return is
much more time consuming than making an assumption (either assumption)
about whether such handlers return. In cases where user-definable
functions are allowed, documentation should state what the expected
return-or-not behavior of the user function should be -- and then not
trust the user anyway.
IMHO, *Proper* use of exceptions can streamline development of software
at all levels, library and otherwise.
Exceptions are part of the language. Third party libraries already throw
them. Therefore, to take full advantage of the re-use that C++ enables,
one is required to write code which deals with exceptions. It is an
unavoidable (but positive) milestone along the road of "software development
development."
--
Kevin J. Hopps e-mail: kjhopps@mmm.com
3M Company phone: (612) 737-3300
3M Center, Bldg. 235-3B-16 fax: (612) 737-2700
St. Paul, MN 55144-1000
Author: hmkirchh@cip.informatik.uni-erlangen.de (Hans-Joachim Kirchhoff)
Date: Wed, 19 Jan 1994 16:23:17 GMT Raw View
Several times now I saw the "old" return errorcode method being preferred
to the new throw catch mechanism. However I cannot see any differences and
certainly no advantages in writing
int errorCode;
errorCode=doSomething();
if (errorCode!=0) {
cleanup();
return errorCode;
}
and
try {
doSomething();
}
catch () {
cleanup();
throw ;
}
The only thing I don't know is, wether catch() is a valid C++ construct. If
it isn't, it should be. catch() would catch all exceptions just as default
"catches" all switch values.
throw ; is a valid C++ expression (at least with the IBM C++/2) and will
rethrow the last thrown object.
It has been argued, that return codes are more "visible" and force the
programmer to "think" about errors. But the above statements are so similar
to each otch other except that the exception mechanism is more powerful,
that I cannot see any advantage in using return codes instead nor any
disadvantage using exceptions.
Hans-Jochen
---
-----------------------------------------------------------------------
Hans-Jochen Kirchhoff | hmkirchh@cip.informatik.uni-erlangen.de
| No signature today | CompuServe 100024,712 | Team OS/2
Fido 2:2490/2525
Author: dag@control.lth.se (Dag Bruck)
Date: 20 Jan 1994 07:15:05 GMT Raw View
In <comp.lang.c++,comp.std.c++> hmkirchh@cip.informatik.uni-erlangen.de (Hans-Joachim Kirchhoff) writes:
>Several times now I saw the "old" return errorcode method being preferred
>to the new throw catch mechanism.
I think the case for exception handling is clearer if we write a *BIG*
function, in this case it has *THREE* statements:
try {
doSomething();
doSomethingElse();
doSomethingAgain();
}
catch (...) {
cleanup();
}
The corresponding code with return values becomes more complex,
assuming that processing must be interrupted if any step fails:
int err;
err = doSomething();
if (err == 0)
err = doSomethingElse();
if (err == 0)
err = doSomethingAgain();
if (err != 0)
cleanup();
Another important advantage is not illustrated here. With return
codes you must do error checking and error handling in every function.
With exception that is not necessary; you can centralize error
handling in the root of a tree of function calls, as the exception
will propagate up the function call chain until caught.
>The only thing I don't know is, wether catch() is a valid C++ construct.
The syntax is catch (...).
-- Dag
Author: eddykim@aludra.usc.edu (Eddy Kim)
Date: 20 Jan 1994 00:57:50 -0800 Raw View
In article <2hlb1p$j8o@nic.lth.se> dag@control.lth.se (Dag Bruck) writes:
>In <comp.lang.c++,comp.std.c++> hmkirchh@cip.informatik.uni-erlangen.de (Hans-Joachim Kirchhoff) writes:
>
[...]
>>The only thing I don't know is, wether catch() is a valid C++ construct.
>
>The syntax is catch (...).
>
> -- Dag
Actually, with BC4.0, catch(...) causes a syntax error. catch( ... ) is
accepted by the compiler. Anyone know if this is valid, a BC bug, or my
copy of the compiler gone mad?
Eddy Kim
Author: g2devi@cdf.toronto.edu (Robert N. Deviasse)
Date: Thu, 20 Jan 1994 13:50:39 GMT Raw View
In article <2hlb1p$j8o@nic.lth.se> dag@control.lth.se (Dag Bruck) writes:
>In <comp.lang.c++,comp.std.c++> hmkirchh@cip.informatik.uni-erlangen.de (Hans-Joachim Kirchhoff) writes:
>>Several times now I saw the "old" return errorcode method being preferred
>>to the new throw catch mechanism.
>
>I think the case for exception handling is clearer if we write a *BIG*
>function, in this case it has *THREE* statements:
>
>try {
> doSomething();
> doSomethingElse();
> doSomethingAgain();
>}
>catch (...) {
> cleanup();
>}
>
>The corresponding code with return values becomes more complex,
>assuming that processing must be interrupted if any step fails:
>
>int err;
>err = doSomething();
>if (err == 0)
> err = doSomethingElse();
>if (err == 0)
> err = doSomethingAgain();
>if (err != 0)
> cleanup();
Or
if ( doSomething() // (or: (err=doSomething()) )
|| doSomethingElse()
|| doSomethingAgain()
)
cleanup();
>
>Another important advantage is not illustrated here. With return
>codes you must do error checking and error handling in every function.
>With exception that is not necessary; you can centralize error
>handling in the root of a tree of function calls, as the exception
>will propagate up the function call chain until caught.
>
True. Anyone who has any doubts as to the usefulness of exceptions should
take a look at the XLISP source code -- exception handling would have
simplified it's design tremendously.
But I think that the point is being made that each method has it's strength
and weakness. Exceptions are not useful (or would likely course problems)
when you are working on the very low level (and need to worry about
asynchronous behaviour) and when you are coding across languages. Exceptions
also are of little value when there is or has to be tight coupling between
the error generator and the error handler (as in the case where the raw
resource is never allocated directly and is only allocated as a part of a
higher level resource which does the error checking for you).
>
> -- Dag
Take care
Robert
--
/----------------------------------+------------------------------------------\
| Robert N. Deviasse |"If we have to re-invent the wheel, |
| EMAIL: g2devi@cdf.utoronto.ca | can we at least make it round this time"|
+----------------------------------+------------------------------------------/
Author: clint@vsfl.demon.co.uk (Ian Cameron Smith)
Date: Fri, 7 Jan 1994 09:08:06 +0000 Raw View
In article <2ghjupINN150@early-bird.think.com> chase@Think.COM writes:
> One thing that will go far in solving your problems is the combination
> of a coding style (or language modification) that defaults all function
> signatures to "raises none", and a compiler which warns when there is
> the possibility of the propagation of a not-in-signature exception.
>
> For instance, this should cause a compile time warning:
>
> class a {...};
> class b {...}; // Classes a and b are not related.
> extern void f() throw (a);
> ...
> void g() throw(b) {
> f(); // If a is thrown, "unexpected" will be called.
> ...
> }
This looks great: my major worry was that when modifying code,
the exception path was invisible. Problem is, do any compilers
support it? We would need this in stable, supported compilers on
a wide range of platforms.
Is this idea being considered by ANSI? Should it be?
--
Ian Cameron Smith clint@vsfl.demon.co.uk
Principal Software Engineer +44 (0)425 474484
Virtual Software Factory LTD
"Go ahead, punk, make my day"
Author: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: Sat, 8 Jan 1994 03:40:58 GMT Raw View
clint@vsfl.demon.co.uk (Ian Cameron Smith) writes:
>chase@Think.COM writes:
>
>> One thing that will go far in solving your problems is the combination
>> of a coding style (or language modification) that defaults all function
>> signatures to "raises none", and a compiler which warns when there is
>> the possibility of the propagation of a not-in-signature exception.
>
>This looks great: my major worry was that when modifying code,
>the exception path was invisible. Problem is, do any compilers
>support it? We would need this in stable, supported compilers on
>a wide range of platforms.
>
>Is this idea being considered by ANSI? Should it be?
Function exception interface specifications were part of the exception
handling extension proposal which was accepted by the ANSI/ISO
committee years ago. They are described in "The C++ Programming
Language" 2nd Ed., sections 9.6, r15.5, and r16.2.2, and in the ARM,
sections 15.5 and 16.2.2.
They should be implemented by all compilers that claim full support for
C++ exception handling.
--
Fergus Henderson | "People who brook no compromise in programming
| languages should program in lambda calculus or
fjh@munta.cs.mu.OZ.AU | machine language, depending." --Andrew Koenig.
Author: chase@Think.COM (David Chase)
Date: 6 Jan 1994 18:06:17 GMT Raw View
I disagree with your conclusions. I used and implemented exception
handling in Modula-3, and played a part in its implementation in one
C++ compiler.
One thing that will go far in solving your problems is the combination
of a coding style (or language modification) that defaults all function
signatures to "raises none", and a compiler which warns when there is
the possibility of the propagation of a not-in-signature exception.
For instance, this should cause a compile time warning:
class a {...};
class b {...}; // Classes a and b are not related.
extern void f() throw (a);
...
void g() throw(b) {
f(); // If a is thrown, "unexpected" will be called.
...
}
There are situations in which you want to have the not-in-signature
exception thrown, but in general, this is not the case, and it is
helpful to have the compiler warn you about it.
The inter-language issues are much trickier, especially in the case of
longjmp. This is so because are different people have different ideas
about what longjmp "means", and each person is certain that they are
right. In the case of an exception thrown "through" C, it is not hard
to get this to work correctly (that is, I have helped to implement an
existence proof), where "correctly" is defined as "the C activation
records are discarded, and propagation of the exception resumes
normally in the C++ code which called the C". (This conflicts somewhat
with the coding/compiler-warning standard proposed above, since the C
routines may not have a signature from which reliable warnings could be
generated, but that's life in a multilingual world.) The way this
works is that the stack is walked, and return PC's are examined to see
if there is an associated exception handler or not (additional handlers
may be generated by the compiler to generate calls to unexpected, and
to implement destruction of local objects). C stack frames have no
associated handler, so they are ignored.
For longjmp thrown through C++, there are (basically) two opinions.
Opinion #1 is: "longjmp called should act like a special exception
thrown, and it should have a defined behavior within
C++, probably running destructors, and perhaps able to
be caught".
Opinion #2 is: "longjmp is called only under special circumstances,
and the stack may be in an unreliable state. Therefore,
the C++ frames (and destructors) are discarded without
any code being run."
There are additional interactions with threads and thread cancellation,
and interactions with silly people who use longjmp to implement
coroutines in C (to be fair, vendors OUGHT to provide the subroutines
which do this so that longjmp's uses will remain consistent with its
definition). I think the bias (in conversations I've had in the past)
is that thread cancellation should interact with longjmp in a style
consistent with opinion #1, thus making longjmp available for the
purposes of opinion #2 (in this case, the interaction is bidirectional
-- cancellation should run destructors, but C frames with associated
cancellation handlers should have those handlers run if the frames are
discarded in the throwing of an exception. This represents a slight
modification to the thread cancellation semantics, since not all the
handlers are one, only those associated with discarded frames. The
implementation of this requires only that the stack walker also consult
a (separate) stack of dynamically registered exceptions. There is some
minor foolishness regarding cancellation handlers registered from
within C++, but since that is not necessary if thread cancellation is
correctly specified (as above) then it need not work particularly well.
Other people replying to this post have suggested
"resumption-exceptions" instead of "termination-exceptions". In this
case, there is experience provided by other systems (Cedar Mesa, for
one) and the conclusions reached by the designers of Modula-2+ and
Modula-3 (based on that experience) is that resumption exceptions are
not worth the effort and complexity. As an (optimizing) compiler
writer, I have an even higher estimate of the complexity and cost, so I
can only agree emphatically. One proposal that I have heard (that
makes a certain amount of sense) is to instead use threads, and in the
case of an exception where one might like to resume, instead awaken
(and wait for reply from) a thread whose purpose it is to deal with
"problems".
It is also probably not necessary to require that exceptions contain an
actual stack trace, since (during development) a debugger (or
debugger-savvy library) should provide that, and after development,
those stack traces should not be seen, and if they are, they will not
be understood, so they are of little use (and if they are of use, then
ship the program linked with the debugger-savvy library).
David Chase, speaking for myself
Thinking Machines Corp.