[comp.lang.c++] Proposal for Exceptions for C++

morrison@eecs.nwu.edu (Vance Morrison) (03/29/88)

A PROPOSAL FOR ADDING EXCEPTION HANDLING TO C++


	I have heard that exception handling for C++ is "in the thinking
about stage".  Unfortunately, not having exceptions is severely camping my
style and so I am writing a preprocessor that adds exception handling
to C++.  It would be VERY nice if the exception handling scheme that
I implement be very similar to the type eventually implemented in the
language (when that happens).  Thus I would like to submit my proposal
to the net to see what everyone thinks.

	This proposal is at present very informal.  After the first
round of discussion, we may want to formalize it.

STRATEGY:

	C and C++ derive their strength from the fact that the language
itself is small.  Instead, most of the power in C and C++ comes from 
library routines.  This give the programmer much more flexibility in
extending the language and also simplifies porting the language.  Thus
I propose changing the language as little as necessary to support 
exceptions.

EXCEPTIONS:

	Exceptions allow the programmer to bypass the normal flow
of control of the program.  By raising an exception, a procedure
can signal an abnormal condition that the procedure is not prepared
to handle.  Raising an exception thus initiates a search up the
call stack for a routine that can "handle" the exception.  If an
exception handler is found, control is handed to it.  If no handler
is found, a "last chance" handler is executed which usually prints
out an error message, produces a stack trace and aborts the program.

	I will not go into the merits of exceptions, but I will state
that they are indispensable for reusable modules because they give
default error handling to the module designer, while allowing the module
user to change error handling.  

PROPOSAL:

	Only three changes to the syntax of the language are necessary

	1) Add the statement '_raise' to the language:
	1) Add the statement 'resume' to the language:
	2) Any statement can now be followed by the following construct
		catch <statement>
	   For example
		i = j/k; catch { printf("divide by zero error\n"); }
	  
	As one might expect, '_raise' raises an exception, while 'catch'
declares a handler for a statement.  Thus whatever statement precedes
'catch' is "protected", in the sense that if any exception is raised
in the protected statement, control is given to the statement after
'catch'.  For example 

	err()
	{
	    if (<error condition>)
		_raise;
	}

	main()
	{
	    err(); catch { printf("error"); }

	    printf("done\n")
	}

	In this example the statement 'err();' is protected, so that
if an exception is raised in 'err();' then "error" is printed.

	After the statement after 'catch' is executed, _raise IS 
IMPLICITLY CALLED.  Thus in the above example after "error" is printed
_raise is implicitly called and the last chance handler is called.  
Thus "done" is NEVER printed out.  To provide the facility to return
to normal flow of control the 'resume' statement is use.  For example

	main()
	{
	    err(); catch { 
		printf("error"); 
		resume; 
		}

	    printf("done\n")
	}

	Now if an exception is raised in 'err()' then "error" will
be printed and control will be resume at 'printf("done");'.

	This behavior is deliberate.  The reasoning is that error 
conditions should NEVER be ignored by default.  Thus if the error
is to be ignored (that is normal flow of control is to resume), the
programmer must explicitly say so by using the resume statement.

SYNTACTIC AMBIGUITIES

In the following example

	for(i=0; i< 10; i++) {
	    printf("%d\n", err());
	    } catch {
		printf("error\n");
		resume;
		}
	    
	Is the 'catch' statement only protecting the printf, or
the whole for(;;) statement?  Here we define it to be just the
printf statement.  If the other meaning was desired, the syntax 
would be

	{
	for(i=0; i< 10; i++) {
	    printf("%d\n", err());
	    } 
	} catch {
	    printf("error\n");
	    resume;
	    }
	    
FUNCTION BODIES

A function body is a legal statement to catch thus

	int main()
	{
	    err();

	    return(0);
	} catch {
	    printf("err\n");
	    return(-1);
	    }

	Is legal and protects the whole function body.  Note
that 'return' is a legal statement in a catch clause, and is
another way to escape from a catch clause (goto will also work).  
	
MAKING IT USEFUL

	The changes to the language were designed to be minimal.
In this raw form it is not very useful since only one unnamed 
exception can be raised.  But using just this simple basis, I have
designed a module that implements named exceptions with parameters
in C++.   Below is an example of a module with three exceptions
FILE_N_FOUND, FILE_NUMBERS, and FILE_ERROR.  In the main program 
the function 'file_call' is protected so that if FILE_ERROR or
FILE_N_FOUND is raised, they are caught and their parameters examined.
All other exceptions will be reraised and the last chance handler
will be invoked.

	I will not explain the workings of my exception module
here.  This example is only meant to show that a sophisticated
exception module can be built from the simple tools I propose.

	SO WHAT DO YOU THINK????

					Vance Morrison
					morrison@accuvax.nwu.edu
					morrison@nuacc.bitnet

****** GENERIC MODULE HEADER FILE *********************************
extern void file_call();


    /* declare FILE_N_FOUND with a parameter 'name' of type 'char_ptr' */
DECLARE_EXCEPTION_1(FILE_N_FOUND, char_ptr, name);
    /* declare FILE_N_FOUND with a parameter 'name' of type 'char_ptr' 
       and a parameter 'number' of type 'int' */
DECLARE_EXCEPTION_2(FILE_NUMBERS, char_ptr, name, int, number);
    /* declare FILE_N_FOUND with no parameters */
DECLARE_EXCEPTION_0(FILE_ERROR);

****** GENERIC MODULE *********************************************
#include "exception.h"
#include "example.h"

	/* the string given here will be printed out using "printf"
	   and the parameters (in order) by the default handler */
CREATE_EXCEPTION(FILE_N_FOUND, "File name '%s' not found");
CREATE_EXCEPTION(FILE_ERROR,  "Generic file error");
CREATE_EXCEPTION(FILE_NUMBERS, "File name '%s' has error at number '%d'\n"); 

void file_call()
{
    FILE_NUMBERS.raise("myfile", 10);
}

****** PROGRAM USING MODULE ****************************************
#include <stdio.h>
#include "exception.h"
#include "example.h"

main()
{
	/* call file_call and catch the FILE_ERROR FILE_NUMBERS */

    file_call(); catch {
	if (FILE_ERROR.is_raised()) {
	    printf("caught file error\n");
	    resume;	
	    }
	if (FILE_NUMBERS.is_raised()) {
	    printf("caught file number error with param %d\n", 
		FILE_NUMBERS.number());
	    resume;	
	    }
	}

    printf("done\n");
}

dhesi@bsu-cs.UUCP (Rahul Dhesi) (03/30/88)

In article <8180006@eecs.nwu.edu> morrison@eecs.nwu.edu (Vance Morrison)
writes:
>A PROPOSAL FOR ADDING EXCEPTION HANDLING TO C++
...
>Thus if the error
>is to be ignored (that is normal flow of control is to resume), the
>programmer must explicitly say so by using the resume statement.

The Ada designers chose not to allow execution of a block to continue
after an exception occurred.  The original Rationale actually gives an
example of how to achieve this effect nevertheless:  by invoking a
procedure again if an exception occurs the first time.

The reason for not allowing a "resume" or equivalent was (I guess) to
avoid undue complexity in programs.  Many exceptions can occur at
unpredictable times (e.g. numeric overflow/underflow at any point
during expression evaluation, I/O error in the middle of a block of
data being written or read), and allowing resumption of the interrupted
statement could be awfully risky.  On the face of it, it also seems to
make life more complicated for the compiler writer to allow a "resume"
statement.

This is something worth keeping in mind.
-- 
Rahul Dhesi         UUCP:  <backbones>!{iuvax,pur-ee,uunet}!bsu-cs!dhesi

lgy@pupthy2.PRINCETON.EDU (Larry Yaffe) (03/30/88)

In article <8180006@eecs.nwu.edu> morrison@eecs.nwu.edu (Vance Morrison) writes:

>A PROPOSAL FOR ADDING EXCEPTION HANDLING TO C++

	[fairly reasonable proposal deleted]

>	SO WHAT DO YOU THINK????
>
>					Vance Morrison
>					morrison@accuvax.nwu.edu
>					morrison@nuacc.bitnet

    As far as I can tell, this proposal for exception handling
(as well as exception handling in many other languages) fails to allow
a very basic form of exception handling that I regard as essential:
the ability for an exception handler to return control to the point
at which the exception was (first) raised and NOT disturb the normal flow
of control.  A simple example from real life involves the following:

    Given a large (numerical analysis) program, determine how many times
a floating point underflow occurs (for particular input) while simultaneously
preserving the usual handling of underflows: setting the result to zero
and continuing.  The desired approach: writing a single underflow exception 
handler which increments a counter, zeros the result, and returns control
to the point where the underflow occured, and then hanging this exception
handler on the top level main routine.  Aborting the calculation at the
first underflow is unacceptable, as is decorating each and every line of
the program with "catch UNDERFLOW; <increment counter>; resume" or some
such variant.

    This sort of approach is possible with UNIX signals, but a good
exception handling extension to the language should surely make resorting
to 'signal' unnecessary.  Obviously, allowing an exception handler to return
to the point where the exception was raised means that one cannot unwind
the call frame looking for a handler, and hence complicates the implementation.
But any exception scheme which prohibits this usage seems (to me) to be
like a two-legged chair - essentially useless.

------------------------------------------------------------------------
Laurence G. Yaffe			lgy@pupthy.princeton.edu
Department of Physics			lgy@pucc.bitnet
Princeton University			...!princeton!pupthy!lgy
PO Box 708, Princeton NJ 08544		609-452-4371 or -4400

nevin1@ihlpf.ATT.COM (00704a-Liber) (03/30/88)

First off, this is not meant as a flame against Vance.  It takes a lot of
effort to come up with a proposal, and it is appreciated.  But, Vance, you
asked for criticism and now you are going to get it! :-)


In article <8180006@eecs.nwu.edu> morrison@eecs.nwu.edu (Vance Morrison) writes:
.A PROPOSAL FOR ADDING EXCEPTION HANDLING TO C++
.
.	Only three changes to the syntax of the language are necessary
.
.	1) Add the statement '_raise' to the language:
.	1) Add the statement 'resume' to the language:
.	2) Any statement can now be followed by the following construct
.		catch <statement>
.	   For example
.		i = j/k; catch { printf("divide by zero error\n"); }
.	  
.	As one might expect, '_raise' raises an exception, while 'catch'
.declares a handler for a statement.  Thus whatever statement precedes
.'catch' is "protected", in the sense that if any exception is raised
.in the protected statement, control is given to the statement after
.'catch'.
...
.	After the statement after 'catch' is executed, _raise IS 
.IMPLICITLY CALLED.
...
.	This behavior is deliberate.  The reasoning is that error 
.conditions should NEVER be ignored by default.

Yes, but since you are already define a 'catch' routine you are obviously
not ignoring the error condition.  For this reason, I feel that the resume
should be implicit and the _raise be explicit (BTW, if you _raise from a
catch routine you should go to the next catch routine if there is one
defined for the first catch routine).  This is a minor point, though.


.SYNTACTIC AMBIGUITIES
.
.In the following example
.
.	for(i=0; i< 10; i++) {
.	    printf("%d\n", err());
.	    } catch {
.		printf("error\n");
.		resume;
.		}
.	    
.	Is the 'catch' statement only protecting the printf, or
.the whole for(;;) statement?  Here we define it to be just the
.printf statement.

This seems to be inconsistent, since if you really wanted to protect only
the printf("%d...), you could declare the catch just after it.  You example
should just catch if 'for' raises an exception (if the printf("%d...) has
an error, no catch routine is called).  If you want both the for statement
and the printf("%d...) to be caught, putbrackets around the for statement
and put the catch after the closing bracket (such as the way you do it for
function bodies--see below).

.FUNCTION BODIES

.A function body is a legal statement to catch thus
.
.	int main()
.	{
.	    err();
.
.	    return(0);
.	} catch {
.	    printf("err\n");
.	    return(-1);
.	    }
.
.	Is legal and protects the whole function body.  Note
.that 'return' is a legal statement in a catch clause, and is
.another way to escape from a catch clause (goto will also work).  
...
.****** PROGRAM USING MODULE ****************************************
.#include <stdio.h>
.#include "exception.h"
.#include "example.h"
.
.main()
.{
.	/* call file_call and catch the FILE_ERROR FILE_NUMBERS */
.
.    file_call(); catch {
.	if (FILE_ERROR.is_raised()) {
.	    printf("caught file error\n");
.	    resume;	
.	    }
.	if (FILE_NUMBERS.is_raised()) {
.	    printf("caught file number error with param %d\n", 
.		FILE_NUMBERS.number());
.	    resume;	
.	    }
.	}
.
.    printf("done\n");
.}

Unfortunately, this does not look all that much different from (using your
file_call() function call as an example):

	if (BAD_VALUE == file_call())
	{
		do some error processing
	}

so I don't see that I am gaining all that much (although this may only be
due to a poor choice of examples).


The power of exceptions comes from being able to move error handling code
away from the actual function calls.  But, if this is to be done in the
'spirit of C++', it must:

a)	Be able to be translated into C

b)	Not incur much (or any, if possible) run-time overhead for code
	which doesn't use exception handlers

c)	Do something which cannot now be done in C++ or do something
	significantly better than it is already done in C++

and I don't think that your method can meet all these goals.  I think that
if you also proposed a method of implementation, we would be able to give
you many more detailed, relevant comments.
-- 
 _ __			NEVIN J. LIBER	..!ihnp4!ihlpf!nevin1	(312) 510-6194
' )  )				"The secret compartment of my ring I fill
 /  / _ , __o  ____		 with an Underdog super-energy pill."
/  (_</_\/ <__/ / <_	These are solely MY opinions, not AT&T's, blah blah blah

ok@quintus.UUCP (Richard A. O'Keefe) (03/30/88)

In article <2229@phoenix.Princeton.EDU>, lgy@pupthy2.PRINCETON.EDU (Larry Yaffe) writes:
> In article <8180006@eecs.nwu.edu> morrison@eecs.nwu.edu (Vance Morrison) writes:
> >A PROPOSAL FOR ADDING EXCEPTION HANDLING TO C++
> 	[fairly reasonable proposal deleted]
>     As far as I can tell, this proposal for exception handling
> (as well as exception handling in many other languages) fails to allow
> a very basic form of exception handling that I regard as essential:
> the ability for an exception handler to return control to the point
> at which the exception was (first) raised and NOT disturb the normal flow
> of control.

This is a very strong requirement.  How would you handle stack overflow
in such a scheme?  (Ok, you could reserve a handler stack, but what do
you do if the handler returns?)  In general, there are lots of exceptions
which cannot sensibly be continued.  Another example:  addressing
non-existent memory.

>  A simple example from real life involves the following:
>     Given a large (numerical analysis) program, determine how many times
> a floating point underflow occurs (for particular input) while simultaneously
> preserving the usual handling of underflows: setting the result to zero
> and continuing.
>     This sort of approach is possible with UNIX signals...

Er, no.  Unless UNIX==BSD.  There is no portable way for a System V
program to find out what sort of floating-point error it got.  (A SIGFPE
might even mean _integer_ divide by zero.)  There are also UNIX systems
providing IEEE floating-point arithmetic where underflows and overflows
and such don't give you exceptions.

> But any exception scheme which prohibits this usage seems (to me) to be
> like a two-legged chair - essentially useless.

ADA is "essentially useless"?  I must remember that.

PL/I has this sort of handling scheme; in order to give you a way of
trying to repair errors so that you could continue, PL/I has ever so
many ON<thing> pseudo-variables.  Try it, you'll hate it.

morrison@eecs.nwu.edu (Vance Morrison) (03/31/88)

	This is in responce to the comments I have seen so far on my
proposal for exception handling for C++.  First may I say that I am glad
to see such a good responce so soon.  Second, I really must apologize for
the incompleteness of my proposal.  I realize that I really did not explain
the semantics of the stack unwinding and other issues well (if at all).
Instead I am relying on the interactive nature of the net to fill in
the gaps as necessary.  Here are some things that need clarification

MEANING OF RAISING AN EXCEPTION
	First let me clarify what happens when an exception is raised.
In my model a search goes on for the most closely nested 'catch' statement
that protects the code in which the exception was raised.t The 'catch' clause
is given control and unless the clase is exited (by using a 'return' 'goto' 
'break' or 'resume' statement), the exception is implictly reraised.
This causes a search for the next layer of 'catch'ing until either a
catch clause is exited or the last chance handler is executed.  

MEANING OF RESUME

	There is some confusion about the meaning of 'resume'  resume
returns control to after the catch clause, NOT TO THE POINT WHERE THE
EXCEPTION OCCURED.   It is IDENTICAL to a goto statement that jumps
right after the 'catch' clause.  That is

	i = j/k; catch {
	    printf("error\n");
	    resume;
			      }
	printf("done\n");

			Is equivalent to 

	i = j/k; catch {
	    printf("error\n");
	    goto out;
				    }
	out:
		printf("done\n");

lgy@pupthy2.PRINCETON.EDU (Larry Yaffe) (03/31/88)

In article <837@cresswell.quintus.UUCP> ok@quintus.UUCP (Richard A. O'Keefe) writes:
>In article <2229@phoenix.Princeton.EDU>, lgy@pupthy2.PRINCETON.EDU (Larry Yaffe) writes:
>>     As far as I can tell, this proposal for exception handling
>> (as well as exception handling in many other languages) fails to allow
>> a very basic form of exception handling that I regard as essential:
>> the ability for an exception handler to return control to the point
>> at which the exception was (first) raised and NOT disturb the normal flow
>> of control.
>
>This is a very strong requirement.  How would you handle stack overflow
>in such a scheme?  (Ok, you could reserve a handler stack, but what do
>you do if the handler returns?)  In general, there are lots of exceptions
>which cannot sensibly be continued.  Another example:  addressing
>non-existent memory.

    Certainly there are exceptions which cannot sensibly be continued.
However, since there are also exceptions which CAN sensibly be continued,
I would greatly prefer any exception handling language extension to
permit continuation.  I regard your example of `non-continuable'
stack overflow with some amusement - since I view this as excellent example
of an exception which should be continuable (if, of course, the handler
can first dynamically extend the stack).  Some operating systems do this
all the time; I would like to be able to do the same thing cleanly and
easily within a good progarmming language.

>>  A simple example from real life involves the following:
>>     Given a large (numerical analysis) program, determine how many times
>> a floating point underflow occurs (for particular input) while simultaneously
>> preserving the usual handling of underflows: setting the result to zero
>> and continuing.
>>     This sort of approach is possible with UNIX signals...

>Er, no.  Unless UNIX==BSD.  There is no portable way for a System V
>program to find out what sort of floating-point error it got.  (A SIGFPE
>might even mean _integer_ divide by zero.)  There are also UNIX systems
>providing IEEE floating-point arithmetic where underflows and overflows
>and such don't give you exceptions.

    Perfectly true.  That's why I'd like to be able to do this solely within
C++.

>> But any exception scheme which prohibits this usage seems (to me) to be
>> like a two-legged chair - essentially useless.

>ADA is "essentially useless"?  I must remember that.

    While my analogy may be just slightly exaggerated (:-), the important
qualifier was "to me".  And yes, PERSONALLY, I regard Ada as
"essentially useless".  Your mileage may vary.

>PL/I has this sort of handling scheme; in order to give you a way of
>trying to repair errors so that you could continue, PL/I has ever so
>many ON<thing> pseudo-variables.  Try it, you'll hate it.

    Your're quite right - nearly everything I know about PL/I convinces me
I don't want to use it.  But that doesn't mean there arn't good ideas
floundering in that swamp of a language.

------------------------------------------------------------------------
Laurence G. Yaffe			lgy@pupthy.princeton.edu
Department of Physics			lgy@pucc.bitnet
Princeton University			...!princeton!pupthy!lgy
PO Box 708, Princeton NJ 08544		609-452-4371 or -4400

morrison@accuvax.nwu.edu.UUCP (Vance Morrison ) (03/31/88)

This is in response to the comments I have seen so far on my
proposal for exception handling for C++.  First may I say that I am glad
to see such a good response so soon.  Second, I really must apologize for
the incompleteness of my proposal.  I realize that I really did not explain
the semantics of the stack unwinding and other issues well (if at all).
Instead I am relying on the interactive nature of the net to fill in
the gaps as necessary.  Here are some things that need clarification

MEANING OF RAISING AN EXCEPTION
        First let me clarify what happens when an exception is raised.
In my model a search goes on for the most closely nested 'catch' statement
that protects the code in which the exception was raised.t The 'catch' clause
is given control and unless the catch is exited (by using a 'return' 'goto' 
'break' or 'resume' statement), the exception is implicitly reraised.
This causes a search for the next layer of 'catch'ing until either a
catch clause is exited or the last chance handler is executed.  

MEANING OF RESUME
	There is come confusion about the meaning of 'resume'.  Resume
returns control to the statement after the 'catch' clause, NOT TO THE
POINT WHERE THE EXCEPTION WAS RAISED!  For example these two structures
are equivalent

	i=j/k; catch {
	    printf("error");
	    resume;
	    }
	printf("done");

is equivalent to 

	i=j/k; catch {
	    printf("error");
	    goto out;
	    }
	out:
	printf("done");


MY RESPONSE TO COMMENTS:
	Here I will respond to some comments I have seen, If I misrepresent
anyone, I am sorry, please let me know.

IN RESPONSE TO LARRY YAFFE'S COMMENTS:
	Larry Yaffe is concerned that my proposal does not allow restart
capability (that is the ability to continue from the point at which the
exception occurred).   In response may I say

	1) My proposal does NOT exclude the possibility of adding a
	   restart capability.  By adding a keyword 'restart' (or whatever)
	   it could be added.  

	2) Second, I have found personally, that restart capability is
	   not indispensable, and usually not very useful.  Usually,
	   you want to restart the problem from the beginning if a fatal
	   error occurs, and if you want the restart from the middle,
	   you can always pass back state information so that the procedure
	   can be called "in the middle",  (I can provide an example
	   if you like).  

	3) Because the exception was raised, the procedure is telling
	   you that it cannot continue normally.  Therefore the handler
	   must "fix" the problem before it can continue.  Providing
	   mechanisms for this is a nasty problem.

	4) Restart is quite a bit more complicated to implement, since
	   destructors CANNOT be called until you can be sure a restart
	   is not pending.  In general its a mess.

    I have found personally that exceptions without restart are QUITE
useful, so we should implement that first.  If we find we need restart,
we can add it as an option.

IN RESPONSE TO NEVIN LIBER'S COMMENTS

	Nevin Had several comments, the first of which was to question
my reasoning on the default '_raise' at the end of a 'catch' statement.
This is a very important point, and I would like to clarify it.  

	First the '_raise' an 'catch' statements are really just tools
for building "real exceptions" and although '_raise' and 'catch' only
implement a single unnamed exception, it is easy to extend as I have done
to provide Named exceptions.  

	Thus a 'catch' clause will almost always have the form

	<statement> catch {
		if (is_raised(EXCEPTION1) {
		    <handle exception 1>
		    resume;
		    }
		else if(is_raised(EXCEPTION2) {
		    <handle exception 2>
		    resume;
		    }
		}

	That is I am only prepared to handle certain exceptions I know
about (or groups of exceptions, or exceptions with certain attributes),
The rest I don't know how to handle so I should pass them on to someone
who does (by reraising).

	The alternative is that the programmer must explicitly reraise
the exception.  The code would look like
		
	<statement> catch {
		if (is_raised(EXCEPTION1) {
		    <handle exception 1>
		    }
		else if(is_raised(EXCEPTION2) {
		    <handle exception 2>
		    }
		else _raise;
		}

	Now if the programmer forgot to put the _raise, he is implicitly
ignoring all errors he can't handle.  THAT IS JUST THE PROBLEM I WANT TO 
SOLVE.  The problem with return codes is that the are all to easy to 
ignore, and numerous bugs and cryptic program crashes are the result.  
The beauty of exceptions is that the default action for an exception is
defined by the module designer and unless the module user EXPLICITLY
catches the exception, that default action is taken.

	Nevin also did not like my binding in the case of

		for(i=0; i<10; i++)
		    printf("test\n"); catch {
			printf("error");
			}

The question is does catch protect just the printf or the entire for
statement.  I defined it to be just the printf (that is catch binds
to the smallest statement).   In some ways this is an arbitrary decision,
but I believe that this binding is the more common, so is a better choice.

	Finally Niven felt that Exceptions that I define have little
advantage over return codes.  He obviously hasn't used them (:-).  
Part of the problem is my example was trivial The big advantages in my 
opinion are:

	1) Exceptions can travel up more that one stack frame, so
	   that you don't need code like the fragment below every
	   time you call a function

		ret = some_function();
		if (ret != SUCCESS)
		   return(ret);

	2) If a programmer uses a module, IF HE DOES NOTHING errors
	   will be handled in a default way (ie printed out).  With
	   error codes, the default action is it ignore the error.

	3) Exceptions can easily have parameters, a BIG plus.


****************************************************************************

	Nevin has asked for an implementation of my exception handling.
I do indeed have an all the major parts of an implementation.   This
implementation is a preprocessor for C++ that uses 'asm()' statements
into the code at proper places which leave lables for the boundaries
of protected regions.  But that is not the point of my proposal, There
are many implementation of exception handling that could be used.  What
I want to establish the syntax for the end result.

	Thats all for now.

						Vance Morrison

morrison@eecs.nwu.edu (Vance Morrison) (03/31/88)

ADDITION TO THE PROPOSAL,

	I recieved a letter which made me realize that a small 
addition to my proposal should be made.  Originally the syntax for
the 'catch' statement was

	<statement> catch <exception handler statement>

I would like to extend it to be

	<statement> catch 
		<exception handler statement> [ else <no exception statement> ]

Where the else clause is optional.
This makes things like looping on exceptions easy

	for(;;) {
	    <statement> catch resume;
			else  break;
	    }


						Vance Morrison

pabuhr@watmsg.waterloo.edu (Peter A. Buhr) (03/31/88)

In article <2229@phoenix.Princeton.EDU> lgy@pupthy2.PRINCETON.EDU (Larry Yaffe) writes:
>In article <8180006@eecs.nwu.edu> morrison@eecs.nwu.edu (Vance Morrison) writes:
>>...
>    As far as I can tell, this proposal for exception handling
>(as well as exception handling in many other languages) fails to allow
>a very basic form of exception handling that I regard as essential:
>the ability for an exception handler to return control to the point
>at which the exception was (first) raised and NOT disturb the normal flow
>of control.  A simple example from real life involves the following:
>
>...

Now, you see, I don't consider this situation as an exception. PL/I
handles this using ON CONDITIONS, which are not exception, but
dynamically bound procedure calls. Exceptions alter the normal
prescribed flow of control in a program. All you want is to invoke a
user defined routine whenever an underflow occurs. Hence, the hardware
calls some routine when it discovers an overflow and you are allowed
to change which routine gets called. In PL/I, there is a essentially a
procedure variable called OVERFLOW (or something like that) which PL/I
initializes to a routine that prints an error message and then aborts
execution which might be done by raising an exception to alter the
flow of control to the system (PL/I does not do this, it just sets
fire to your program and walks away). You are allowed to reassign the
value of this procedure variable to your own routine.  So you can
count the number of overflows and possible reset the erroneous value
to anything you like and then return back to the hardware to continue
execution. PL/I makes it all complex by using dynamic binding to determine
which procedure named OVERFLOW to executed, but that's another issue.

eric@snark.UUCP (Eric S. Raymond) (03/31/88)

In article <8180006@eecs.nwu.edu>, morrison@eecs.nwu.edu (Vance Morrison) writes:
>PROPOSAL: [for exception-handling in C]

I like the basic idea of a C analogue of LISP throw/catch, but:

	1) I'm not clear how this offers more capability than setjmp/longjmp.

	2) I think your catch syntax is un-C-like; no other construct in
	   the language makes that kind of link between adjacent statements.

I think that making 'catch' an if-like construct would fit C better; i.e
instead of

>		i = j/k; catch { printf("divide by zero error\n"); }

I suggest

	catch { i = j / k; } do { printf("divide by zero error\n"); }

I think this is easier to read, and resolves in a more elegant way the
syntactic difficulties you've noted.

On the positive side, I do believe (from my LISP experience) that this is a
suitable 'most primitive' exception-handling mechanism for environments with
only a single control thread (for multiple-thread environments you need to
support *named* exceptions, because a common global exception-type variable
could get fandangoed on).
-- 
      Eric S. Raymond                     (the mad mastermind of TMN-Netnews)
      UUCP: {{uunet,rutgers,ihnp4}!cbmvax,rutgers!vu-vlsi}!snark!eric
      Post: 22 South Warren Avenue, Malvern, PA 19355   Phone: (215)-296-5718

bruce@psldev.Dayton.NCR.COM (bruce frederiksen) (04/01/88)

In article <837@cresswell.quintus.UUCP> ok@quintus.UUCP (Richard A. O'Keefe) writes:
>In article <2229@phoenix.Princeton.EDU>, lgy@pupthy2.PRINCETON.EDU (Larry Yaffe) writes:
>> In article <8180006@eecs.nwu.edu> morrison@eecs.nwu.edu (Vance Morrison) writes:
>> >A PROPOSAL FOR ADDING EXCEPTION HANDLING TO C++
>> 	[fairly reasonable proposal deleted]
>>     As far as I can tell, this proposal for exception handling
>> (as well as exception handling in many other languages) fails to allow
>> a very basic form of exception handling that I regard as essential:
>> the ability for an exception handler to return control to the point
>> at which the exception was (first) raised and NOT disturb the normal flow
>> of control.
>
>This is a very strong requirement.  How would you handle stack overflow
>in such a scheme?  (Ok, you could reserve a handler stack, but what do
>you do if the handler returns?)  In general, there are lots of exceptions
>which cannot sensibly be continued.  Another example:  addressing
>non-existent memory.

You've offered a solution to the stack overflow problem.  Your other
example, addressing non-existent memory, is one that I could use in a
project I'm working on here.  What I want to do is dynamically allocate
shared memory blocks that are used as shared heap space by several
processes.  When one process finds the heap exhausted, he creates a new
shared memory block (much like malloc doing an sbrk), attaches it,
initializes it and allocates his whatever.  He then chains this new
whatever to the shared list of whatevers that these processes are using.

Now another process takes control and starts reading down this list and
jumps off the end into the new shared memory block that he hasn't attached
yet.  Easiest way to handle this would be to have an exception handler in
each process to handle "addressing non-existent memory".  It just attaches
the new shared memory block and then returns back to the program as if
nothing had happened.

On a whole, I don't think that an exception mechanism should be built into
the language.  Any exception mechanism can be built in C++ to do whatever
you or I or anybody else might want except for two things:

	1. The standard setjmp/longjmp routines don't execute destructor
	   functions for objects in the intermediate (aborted) functions.

	2. (as you have said) Numerical exceptions (divide by 0, overflow,
	   underflow) are not reported in C.  You could write protected
	   numbers with operators that check for these things, but it may
	   not be as efficient as it could be when there is hardware
	   support for these.

I think that the first problem is at the real crux of exception mechanisms.
Once it is solved, exception mechanisms in the form of libraries can be
written without cluttering the language.  Then if you are not happy with
how I've implemented my exception mechanism you are not forced to use it.

One solution to the first problem might involve using two parallel stacks:
one for objects with destructors and one for everything else.  Then the
longjmp can fire up the destructor functions of all the objects in the one
stack (down to the function being returned to) without worrying about all
of the other clutter in the stack.  This assumes that the destructor
function and length of any object on the stack can be determined given just
its address.  Virtual functions handle this just fine.  So if you're
willing to live with virtual destructors and length functions this can all
be done in C++ without changing the language.

If anybody else has a solution to the setjmp/longjmp problem, I'd like to
hear it.

-bruce.frederiksen@Dayton.NCR.COM
 NCR Corp, SE-Retail, Dayton

steve@vsi1.UUCP (Steve Maurer) (04/01/88)

Article 303 of comp.lang.c++:
Path: vsi1!steve
From: steve@vsi1.UUCP (Steve Maurer)
Newsgroups: comp.lang.c++
Subject: Re: Proposal for Exceptions for C++
Message-ID: <457@vsi1.UUCP>
Date: 31 Mar 88 19:26:58 GMT
References: <8180006@eecs.nwu.edu> <2229@phoenix.Princeton.EDU> <837@cresswell.quintus.UUCP> <2243@phoenix.Princeton.EDU>
Reply-To: steve@vsi1.UUCP (Steve Maurer)
Organization: Vicom Systems Inc. San Jose, Cal.
Lines: 71


    I have a question.   What's wrong with doing this?

	Have an exception keyword: "exception", placed before the
	exception processing function.

	Have a identifier following which is the "name" of the
	exception (much like structure identifiers).

	Have a couple of predefined identifiers for the most common
	exceptions: divide by zero, stack overflow, etc.

	use "raise" with these identifiers.

	redefine "resume" to not disturb the flow of control


Example Of Use:

extern int counter;
extern int result;

exception userfoobar		/* a user defined exception */
{
counter--;
}

exception zerodivide		/* zerodivide is a predefined exception	*/
{				/* ...raised on divide by zero errors	*/
counter++;
result = 0;
if (counter > 200) exit();
resume;	/* actually a redundant statement, resume is implicit	*/
}

exception noresume		/* noresume is a predefined exception	*/
{				/* ...raised when resuming generates an	*/
printf("error could not resume\n");		/* immediate error	*/
exit(-1);
}	// exit is also redundant in this case, a few predefined
	// exceptions are automatically fatal (they exit no matter what)

//---------------
int counter, result;

main(int ac; char **av)	/* example use	*/
{
float foo[1000], bar[1000];
int i;
getinputarray(foo);		/* inputs data from somewhere	*/
getinputarray(bar);

for (i = 0; i < 1000; i++)
    {
    result = foo[i] / bar[i];		/* raises exception on / by 0	*/
	/* any other processing	*/	/* increments counter		*/
    }

raise zerodivide;		/* raise a divide by zero exception	*/
raise userfoobar;		/* raise a userfoobar exception		*/
}


    This is simple to use, powerful (though not quite complete), and
would not be too much of a pain to translate into any reasonable C 
implementation (using signals/longjmps).

					Steve Maurer
					tolerant!vsi1!steve
					octopus!vsi1!steve

daniels@teklds.TEK.COM (Scott Daniels) (04/01/88)

In article <2229@phoenix.Princeton.EDU> 
lgy@pupthy2.PRINCETON.EDU (Larry Yaffe) writes:
>...  As far as I can tell, this proposal for exception handling
>(as well as exception handling in many other languages) fails to allow
>a very basic form of exception handling that I regard as essential:
>the ability for an exception handler to return control to the point
>at which the exception was (first) raised and NOT disturb the normal flow
>of control....

The problem with this approach is that it dictates a substantial change
in the C language implementation requirements.  Without this capability,
all C variable accesses may be reached either by a global access (typically
an absolute address), or as addresses relative to a stack pointer (for auto
variables).  With this requirement, the "catch statement" must be able to 
access the locals for the function in which it is executing, while still
maintaining a stack depth that allows returning to the original function.
Note that this problem exists by implication in the stated design since 
the default handler "prints the stack"...  If the stack is abandoned (as
an alternate return mechanism might reasonably do), there is nothing left 
to print.

Managing a stack that happens from this code would be a nightmare:

main(argc, argv)
 int argc;
 char **argv;
{
 something() 
   catch { 
	foo( mumble(), fumble() ) 
	  catch {
		printf("Oops: %s caught in catch\n", argv[0]); 
		if( random() > 0.5 ) resume_original;
		if( random() > 0.5 ) resume_here;
		printf( "catch in catch completed.\n" );
		} 
	printf( "catch completed.\n" );
	}
 printf( "main completed.\n" );
}

-Scott Daniels		daniels@teklds.UUCP

lgy@pupthy2.PRINCETON.EDU (Larry Yaffe) (04/01/88)

In article <17935@watmath.waterloo.edu> pabuhr@watmsg.waterloo.edu (Peter A. Buhr) writes:
>In article <2229@phoenix.Princeton.EDU> lgy@pupthy2.PRINCETON.EDU (Larry Yaffe) writes:
	[An example involving counting underflows]

>Now, you see, I don't consider this situation as an exception. PL/I
>handles this using ON CONDITIONS, which are not exception, but
>dynamically bound procedure calls. [More PL/I info deleted]

    I am curious about whether most people agree with this distinction
concerning the meaning of an exception.  To me, an "exception" indicates
that something exceptional happened, possibly needing some intervention
(a handler), but not necessarily something "disasterous" (i.e., requiring
major alteration in the flow of control).  From the language design
viewpoint, it seems arbitrary and unpleasant to force a distinction
between "dynamically bound procedure calls" - possibly triggered by
hardware traps - and "real" exceptions.  Other opinions?

------------------------------------------------------------------------
Laurence G. Yaffe			lgy@pupthy.princeton.edu
Department of Physics			lgy@pucc.bitnet
Princeton University			...!princeton!pupthy!lgy
PO Box 708, Princeton NJ 08544		609-452-4371 or -4400

gore@eecs.nwu.edu (Jacob Gore) (04/01/88)

/ comp.lang.c++ / lgy@pupthy2.PRINCETON.EDU (Larry Yaffe) / Mar 31, 1988 /
>In article <17935@watmath.waterloo.edu> pabuhr@watmsg.waterloo.edu (Peter A. Buhr) writes:
>>Now, you see, I don't consider this situation as an exception. PL/I
>>handles this using ON CONDITIONS, which are not exception, but
>>dynamically bound procedure calls.
>
>[...] To me, an "exception" indicates
>that something exceptional happened, possibly needing some intervention
>(a handler), but not necessarily something "disasterous" (i.e., requiring
>major alteration in the flow of control).  From the language design
>viewpoint, it seems arbitrary and unpleasant to force a distinction
>between "dynamically bound procedure calls" - possibly triggered by
>hardware traps - and "real" exceptions.

Actually, I think it's a very nice distinction.  There are languages where one
can make heavy use of procedures activated on certain conditions -- for
example, when a variable's value changes.  Their activation not only indicates
nothing disasterous, but nothing exceptional, either.

I am having some trouble classifying your example (counting overflows).
Overflows are USUALLY associated with exceptional situation.  However, if you
expect that overflows are not unlikely to happen -- if you didn't, why would
you want to count them? -- then I would not consider overflows exceptional.

(I'm not sure what all this has to do with dynamic binding of procedure calls,
but that's another story...)

Jacob Gore				Gore@EECS.NWU.Edu
Northwestern Univ., EECS Dept.		{oddjob,gargoyle,ihnp4}!nucsrl!gore

gore@eecs.nwu.edu (Jacob Gore) (04/02/88)

/ comp.lang.c++ / steve@vsi1.UUCP (Steve Maurer) / Mar 31, 1988 /
>What's wrong with doing this?
...
>Example Of Use:
...
>exception userfoobar		/* a user defined exception */
>{
>counter--;
>}

Well, it's not that it's "wrong," but that it could be so much better.
Exceptions can be objects in their own right.  That would permit one to have
sets, prioritized lists, whatever of exceptions. 

Your solution provides very inflexible exception handling.  You are trying to
make an exception handler look like a procedure.  But in C, that makes it even
more restrictive than the arrangement used in Ada: Not only are you
restricting how exceptions can be organized, but you are also allowing for
only one possible way to handle each exception! 

Jacob Gore				Gore@EECS.NWU.Edu
Northwestern Univ., EECS Dept.		{oddjob,gargoyle,ihnp4}!nucsrl!gore

keith@nih-csl.UUCP (keith gorlen) (04/02/88)

In article <8180002@accuvax.nwu.edu.UUCP>, morrison@accuvax.nwu.edu.UUCP (Vance Morrison ) writes:
-> 
-> 	Nevin has asked for an implementation of my exception handling.
-> I do indeed have an all the major parts of an implementation.   This
-> implementation is a preprocessor for C++ that uses 'asm()' statements
-> into the code at proper places which leave lables for the boundaries
-> of protected regions.

That's the easy part.  How does your implementation arrange to execute
the destructors of class objects that go out of scope as a result of
an exception?
-- 
	Keith Gorlen			phone: (301) 496-5363
	Building 12A, Room 2017		uucp: uunet!ncifcrf.gov!nih-csl!keith
	National Institutes of Health	Internet: keith%nih-csl@ncifcrf.gov
	Bethesda, MD 20892

morrison@eecs.nwu.edu (Vance Morrison) (04/03/88)

Hello,

	This is in reply to Steve Maurer's suggesting on making 
exceptions look like functions.

There are several problems
	1) It does not allow more than one exceptions handler to be
	   defined for a given exception.  (if a given exception is
	   declaired more than once, which one should it choose?)

	2) MOST times, when an exception is raised, you want to
	   return to the scope of a functions somewhere above you 
	   on the stack, how do you do this?  (longjumps won't work
	   since they do not call destructors properly)

	3) Often you need access to variables that are in the scope
	   of functions above you in the call stack to handle the
	   error properly.  How can you do this?

	In my mind, exceptions are FLOW OF CONTROL constructs.  They
allow you to 'goto' places out of your scope in a controled way.  
exceptions are not functions or data structures, they are more like
fancy 'goto' statements.

					Vance Morrison
					Northwestern Univ.

dwaitzma@bbn.com (David Waitzman) (04/05/88)

In nnsc.nsf.net pub/exceptions.PS (available via anonymous ftp) is a
(drafty) paper (in postscript) on adding exceptions to a language
built upon C++: Avalon.  The paper also examines adding exceptions to
C++ directly.  Some of the ideas presented in the paper have been
discussed with (the) Bjarne.  If anyone wants, I can mail them a copy
of the paper directly.


-david
-------
The paper was written when I was a student at Carnegie Mellon.
BBN Labs, Inc. has no opinion on this (as far as I know).

johna@cognos.uucp (John Anderson) (04/06/88)

In article <8180006@eecs.nwu.edu> morrison@eecs.nwu.edu (Vance Morrison) writes:
>A PROPOSAL FOR ADDING EXCEPTION HANDLING TO C++

What about destructors??  Aren't they one of the key problems
to be delt with in an exception handling mechanism?

-- 
John Anderson, Cognos Incorporated, 
P.O. Box 9707, 3755 Riverside Drive,
Ottawa, Ontario, CANADA  K1G 3Z4
(613) 738-1440	   decvax!utzoo!dciem!nrcaer!cognos!johna

wyant@apollo.uucp (Geoffrey Wyant) (04/06/88)

  I have two problems with the exception mechanisms proposed so
far.  First and foremost, most of them don't seem to have been
well thoughtout semantically.  The majority of them seem to think
up some syntax for raising and catching exceptions and supply a
few trivial examples to illustrate the semantics.  Nobody has really
said what an exception is, is it a globally defined indentifier,
an object or what ?  Secondly, or exception handlers statically
or dynamically bound ?  Designing a good exception mechanism is
difficult and touches on a large number of semantic issues.  

  The other problem I have with the proposals is that these ill-defined
exceptions aren't being treated first class entities in the language.
Why should I be able to subclass exceptions.  Its very usefull to do.
This enables you to organize and catagorize exceptions.

  Well, enough ramblings and rantings.

        -- Geoff
-- 

    UUCP: ...{yale,mit-eddie}!apollo!wyant

pierson@encore.UUCP (Dan Pierson) (04/07/88)

In article <22999@bbn.COM> dwaitzma@vax.BBN.COM (David Waitzman) writes:
>In nnsc.nsf.net pub/exceptions.PS (available via anonymous ftp) is a
>(drafty) paper (in postscript) on adding exceptions to a language
>built upon C++: Avalon.  The paper also examines adding exceptions to
>C++ directly.  Some of the ideas presented in the paper have been
>discussed with (the) Bjarne.

Your proposal is almost the best so far.  It certainly does the best job
of integrating exceptions into the C++ class system.  However, there is
one part of the proposal which I strongly dislike: exceptions are only
propogated a single call level.  This causes code written using
exception handling to be almost as verbose, hard to read, and hard to
maintain as code using special return values.  The biggest possible win
with this approach is that instead of having to follow every call which
wishes to check errors with a block of error handling code, you have to
follow every block which wishes to check errors with a block of error
handling code.  Much of this code will include something like:

	...
	default: ex->raise();
	...

In fact, in many cases that may be the entire contents of the exception
clause! 

Consider the following case: We're about to perform an atomic action of
some sort.  The top level code sets up the atomic block in some way and
calls one or more lower level routines to perform the action.  These
routines then call other routines, etc.  Several call levels down a
routine runs out of a critical resource, say memory or disk space.  In
the context of this application the correct thing to do is back out the
current action and let the top level routine try and get more resource,
say by cleaning up or asking the user for help.

What I'd like to do is have all the lower level routines proceed with no
knowledge of this possible problem __except__ for providing finalization
methods which undo all of their work (which should only be resource
allocation at this level).  The exception would propogate up to the
handler in the top level routine, cleaning things up as it goes, and the
top level handler would then try to get more of the resource and try the
action again (or raise its own exception).

With your proposal, every single routine would have to have an exception
block which explicitly re-raised all exceptions.  This would add to the
code bulk, thus camouflaging the local algorithms to no benefit.
-- 

In real life: Dan Pierson, Encore Computer Corporation, Research
UUCP: {talcott,linus,necis,decvax,ihnp4}!encore!pierson
Internet: pierson@multimax.arpa

steve@vsi1.UUCP (Steve Maurer) (04/08/88)

>	This is in reply to Steve Maurer's suggesting on making 
>exceptions look like functions.
>
>There are several problems
>	1) It does not allow more than one exceptions handler to be
>	   defined for a given exception.  (if a given exception is
>	   declaired more than once, which one should it choose?)

    Presumably, this would fail to compile.  If you wanted two behaviors
out of your exception handler, you would have to use an 'if' statement.


>	3) Often you need access to variables that are in the scope
>	   of functions above you in the call stack to handle the
>	   error properly.  How can you do this?

	The C++ compiler (translator) would look to upper scopes for
    the variable name you requested.  If found, it would record the
    procedure name, variable offset, and record this as arguments to
    a run-time routine that would return the variable's value.   Of
    course this would work for automatic variables only.


>	2) MOST times, when an exception is raised, you want to
>	   return to the scope of a functions somewhere above you 
>	   on the stack, how do you do this?  (longjumps won't work
>	   since they do not call destructors properly)

    Even though the original article didn't have a keyword to do this,
it should have been added.  As for implementation, though this is
difficult, it could be done.   Basically, the C++ translator would
have to generate a call to one of 2 runtime functions, which either
searched for a named procedure, or simply returned up one or more
levels.  Both would have to call destructors during run time.

    Of course, this *still* doesn't give us everything we want,
but I have been laboring under the assumption that most people
want a construct that can be translated into C ( by one of the
popular translators out there ), and I don't think that you can
get everything you want and still do this.

					Steve Maurer

p.s. I don't think anything we discuss here, ever *really* has a chance
	of being adopted, so I'm not sure why I bother.

jima@hplsla.HP.COM ( Jim Adcock) (04/08/88)

| That's the easy part.  How does your implementation arrange to execute
| the destructors of class objects that go out of scope as a result of
| an exception?

Well now, there seems to be several people who claim to have solutions
to the C++ exception problem, but I have yet to see anyone explain how
to call destructors for the objects going out of scope.

If someone HAS figured out a good solution to this problem, how about a 
quick explanation?

[under the restriction that any object on the stack (that needs to be
 destroyed when an exception trashes its environment) has a vtable,
 this problem becomes quite doable.  Anybody have found a less 
 restrictive (and reasonable) solution? ]

tecot@Apple.COM (Ed Tecot) (04/08/88)

In article <22999@bbn.COM> dwaitzma@vax.BBN.COM (David Waitzman) writes:
>In nnsc.nsf.net pub/exceptions.PS (available via anonymous ftp) is a
>(drafty) paper (in postscript) on adding exceptions to a language
>built upon C++: Avalon.  The paper also examines adding exceptions to
>C++ directly.  Some of the ideas presented in the paper have been
>discussed with (the) Bjarne.  If anyone wants, I can mail them a copy
>of the paper directly.

Funny how I see this and get your mail at the same time...
Yes, Stanford is near Apple.  I may not be around the weekend of the 16th,
but you can try to get in touch with me.

My phone is 408-973-5418 work and 408-739-4240 at home.

Please bring a copy of the paper too.

					_emt

keith@nih-csl.UUCP (keith gorlen) (04/09/88)

In article <6590029@hplsla.HP.COM>, jima@hplsla.HP.COM (              Jim Adcock) writes:
-> Well now, there seems to be several people who claim to have solutions
-> to the C++ exception problem, but I have yet to see anyone explain how
-> to call destructors for the objects going out of scope.
-> ...
-> [under the restriction that any object on the stack (that needs to be
->  destroyed when an exception trashes its environment) has a vtable,
->  this problem becomes quite doable.  Anybody have found a less 
->  restrictive (and reasonable) solution? ]

How does heving a vtable help?

-- 
	Keith Gorlen			phone: (301) 496-5363
	Building 12A, Room 2017		uucp: uunet!ncifcrf.gov!nih-csl!keith
	National Institutes of Health	Internet: keith%nih-csl@ncifcrf.gov
	Bethesda, MD 20892

chris@mimsy.UUCP (Chris Torek) (04/10/88)

In article <6590029@hplsla.HP.COM> jima@hplsla.HP.COM (Jim Adcock) writes:
>Well now, there seems to be several people who claim to have solutions
>to the C++ exception problem, but I have yet to see anyone explain how
>to call destructors for the objects going out of scope.

What is needed is a generic `unwind-protect' (not necessarily in
the language itself, but certainly for the compiler).  The code
generated will then look something like this (pseudo C++ syntax):

	/* object o; protection p: */
	if (unwind_protect(&p)) {
		o.~o();
		unwind_resume(&p);
	}
	o.o();
	...
	o.~o();
	remove_protection(&p);

It is important that the `unwind_protect' and `remove_protection'
operations be fast, as these could occur quite often.  In stack
architectures this can usually be done with `protection objects'
and address arithmetic.  Something like the following can be made
to work:

	struct protection {
		struct protection *outer;	/* outer protection(s) */
		jmp_buf label;			/* context */
	} InnermostProt;	/* current innermost active protection */
	#define unwind_protect(p) \
		((p)->outer = InnermostProt, \
		 InnermostProt = (p), \
		 setjmp((p)->label))
	#define remove_protection(p) (InnermostProt = (p)->outer)
	/*
	 * unwind compares each stack frame with InnermostProt
	 * to see if there is a protection frame there, and if so
	 * jumps to it. unwind_resume can be optimised if necessary
	 * by noting when protections belong to the same frame.
	 */

There remains the problem that the object may be in any of three
`improper' states (not constructed, only partially constructed, or
partially destroyed) at the time the exception causes the stack to be
unwound past the protection markers.  There appear to be only two
cures: either defer the unwinding (and hence the entire exception
process) until the object is stable, or ensure that the con- and
de-struction processes use only atomic operations.  The former approach
fails in the general case (exceptions during -struction either
oscillate or fail utterly); the latter is just plain hard, even
with help from the programmer.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

jima@hplsla.HP.COM ( Jim Adcock) (04/12/88)

|| [under the restriction that any object on the stack (that needs to be
||  destroyed when an exception trashes its environment) has a vtable,
||  this problem becomes quite doable.  Anybody have found a less 
||  restrictive (and reasonable) solution? ]
|
|How does having a vtable help?

The short [partial] answer: Having a vtable for an cleanup-able object -- 
aka insisting that objects that are to be destroyed when an exception trashes 
its environment have one or more virtual virtual functions defined on
that object -- allows "all" of the questions necessary to
sucessfully destroy an object to be answered by the "object" itself.
The questions that must be answered by that object are probably:
1) am "I" an object?
2) do I need to have a "destruction" operation applied to me upon my death?
3) what is the "destruction" operation that needs to be applied upon my death?
4) how big am I?

Also, note the following discussion [which I generally agree with] before
I give more details:

|On a whole, I don't think that an exception mechanism should be built into
|the language.  Any exception mechanism can be built in C++ to do whatever
|you or I or anybody else might want except for two things:
|
|	1. The standard setjmp/longjmp routines don't execute destructor
|	   functions for objects in the intermediate (aborted) functions.
|
|	2. (as you have said) Numerical exceptions (divide by 0, overflow,
|	   underflow) are not reported in C.  You could write protected
|	   numbers with operators that check for these things, but it may
|	   not be as efficient as it could be when there is hardware
|	   support for these.
|
|I think that the first problem is at the real crux of exception mechanisms.
|Once it is solved, exception mechanisms in the form of libraries can be
|written without cluttering the language.  Then if you are not happy with
|how I've implemented my exception mechanism you are not forced to use it.
|
|One solution to the first problem might involve using two parallel stacks:
|one for objects with destructors and one for everything else.  Then the
|longjmp can fire up the destructor functions of all the objects in the one
|stack (down to the function being returned to) without worrying about all
|of the other clutter in the stack.  This assumes that the destructor
|function and length of any object on the stack can be determined given just
|its address.  Virtual functions handle this just fine.  So if you're
|willing to live with virtual destructors and length functions this can all
|be done in C++ without changing the language.
|
|If anybody else has a solution to the setjmp/longjmp problem, I'd like to
|hear it.

The parts of the above discussion I don't totally agree with are 1) I wouldn't 
consider support of two parallel stacks to be an acceptable solution, 
since in my mind this would diverge too much from standard language
implementations and 2) I don't consider lack of detection of an error condition
E on a machine M's implementation of C[++] in an operating system U to be part
of the "exception handling" problem in the C++ language.  Note, however,
I might be able to be convinced that the second stack [for objects requiring
destruction] could be successfully simulated by maintaining a linked-list
of destructable objects on heap (although this is starting to sound enough
like other "garbage-collecting" languages to make me nervous).  Alternately,
note that one stack would be sufficient if the objects on a "dead" region 
that stack that need to be "destroyed" can be reliably detected.  If you
were to use the heap-based approach, I think your heap would need to be
buddy-system, as I believe the g++ implementation of heap is.

So to my mind two "solutions" to the "clean-up on exception" problem might be:

1) maintain a linked list of "destructable" objects in heap space, portions
   of that list to be "destructed" when their corresponding environments get
   killed by an exception.

2) provide a way to "reliably" detect objects in the standard stack that need
   to be destroyed when their corresponding environments get killed by an 
   exception.  If the following psuedo-code can be "reliably" implemented 
   then the standard stack can be used:

pointer p = "bottom"-of-killed-region of stack
while p <= "top"-of-killed-region-of stack
{
    if (is-valid-destructable-object(*p) && p->needs-destruction())
    {
        pstep = p->size();
        p->destroy();
        p += pstep;
    }
    else ++p;
}
  
If this is indeed a "solution", one might productively ask what it is I think
I am solving (since every author addressing C++ "exceptions" seems to have a
different idea of what the problem "is" and "isn't").

I don't consider the following to be a necessary part of MY conception of the 
essential "exception-handling" problem in C++ :

- provide the "best" possible syntax for exception handling as a new construct
  within C++
- provide a new, alternative, unstructured flow of control mechanism to 
  programmers who have been cruelly denied the use of goto statements by
  the force of public opinion :-)
- provide a "built-in" exception handling capability in C++
- provide an exception handling technique [routines, libraries ...] that 
  can be trivially ported between machines.
- provide a single base class for all "objects"
- provide an "exception handling capability" that will still succeed in the
  face of a deliberately malicious user[/abuser] [Since C/C++ already allows
  deliberately malicious users to hose pretty much whatever they want to hose]
- provide for the "trivial" programming of the failure case where an exception
  is raised during the construction/destruction of an object which itself needs
  to be cleanly destroyed when an exception occurs.

I do consider the following to be a necessary part of MY conception of the
essential "exception-handling" problem in C++:

- provide a reasonably simple way for motivated C++ programmers to catch and 
  recover from general software or hardware problems that could not be 
  reasonably anticipated and coded as part of the standard coding process.  
  The goal is to recover from what would otherwise be an untimely death of the
  executing program.  Since this definition of "exceptions" describes
  a truly "exceptional" situation, the cost for recovering from this 
  unfortunate circumstance need not be optimal in terms of recovery time.
- provide "exception handling" that can be used to catch errors in existing 
  C/C++/Pascal/Fortran/etc libraries without having to recompile these libraries
  [IE no change in the traditional design of global/stack/heap spaces]
- provide exception handling at "zero cost" for people who aren't using it.
- provide exception handling at "VERY low cost" in terms of execution speed
  when "exception" conditions aren't occuring.
- provide exception handling at "zero cost" in size and speed for objects 
  that don't need to be "destructed" when an exception occurs.
- provide exception handling at "very low cost" in size for objects that do
  need to be "destructed" when an exception occurs
- provide for destruction of the "dying" objects on the stack at "reasonable"
  cost when an exception destroys their environment.

Given the above definition of the "exception handling" problem my claim is
that a sufficient solution is probably something like:

1) A setjump/longjump type capability.

2) Foundation class[es] which define the above discussed virtual functions
   along with any necessary class variables to implement those virtual
   functions.

3) A routine to search the dying region of stack for "objects" that require
   "destruction" when an exception kills their region of stack.

4) Ways for exceptional conditions to alter flow-of-control and invoke
   the stack-sweep routine over the appropriate dying region of stack.

So I'm not [yet] convinced that exception handling capabilities should be
"built-in" to C++.

I have a very crude, preliminary, [not-very-portable], example of a C++
routine to sweep a "dying" region of stack and to search-and-destroy those
objects requiring destruction; along with a compatible [minimal] base class.
This example might be of some interest to people very motivated to work 
on "solving" the "exception handling" problem on their machines.