Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Path: utzoo!watmath!clyde!burl!ulysses!allegra!mit-eddie!think!harvard!seismo!elsie!cecil!keith From: keith@cecil.UUCP (keith gorlen) Newsgroups: net.lang.c++ Subject: Exception handling in C++ Message-ID: <51@cecil.UUCP> Date: Tue, 25-Feb-86 09:28:13 EST Article-I.D.: cecil.51 Posted: Tue Feb 25 09:28:13 1986 Date-Received: Fri, 28-Feb-86 07:47:09 EST Organization: NIH-CSL, Bethesda, MD Lines: 347 C++ is a terrific language, but I was a bit disappointed initially that it included no facilities for exception handling -- but wait -- maybe "you can program that yourself"! My favorite exception handling mechanism is the SIGNAL-ENABLE construct of BLISS-11. Its reasonably efficient and flexible, and it seems to have inspired Ada's RAISE-EXCEPTION construct. A similar facility for C++ works as follows: BEGINX // start of exception block statements that might cause an exception to be raised EXCEPTION exception handlers; format same as body of switch statement ENDX // end of exception block As a trivial example: #include #include "exception.h" const int exception1 = 1; void f() { RAISE(exception1); } main() { BEGINX f(); cerr << "This will never happen!\n"; EXCEPTION case exception1: cerr << "exception1 handled\n"; ENDX cerr << "End of program\n"; } will output: exception1 handled End of program Of course, if no exception is raised in the first part of the block, the handlers following the EXCEPTION keyword are not executed. When an exception is raised, the case with the same exception code (if any) is executed. Unless the flow of control is altered by a return, break, continue, etc., execution continues with the first statement after the ENDX whether or not an exception occurred, and whether or not a matching case was found. BEGINX...ENDX blocks may be nested within either part of a BEGINX...ENDX block; i.e., the exception handlers may contain exception blocks. An exception is raised by executing the statement RAISE(code), causing control to pass to the most recently executed exception block. An exception handler may reference the current exception code by the name EXCEPTION_CODE. If no case matches the current exception code, it can be propagated up to the next most recently executed exception block by including the statement: default:RAISE(EXCEPTION_CODE); in the exception block. The shar file attached to the end of this article contains my implementation of this and a test program. I would like to hear people's comments on the following points: 1. Will this implementation work in general, or have I forgotten something? Are there some dire consequences of using setjmp/longjmp in this fashion? Note that destructors will not be called when a block is exited by doing a RAISE. 2. In Ada, if an EXCEPTION block has no handler for the raised exception, the exception is automatically propagated up to the next level. In my implementation, control goes to the statement after the ENDX. I think Ada's way is better. Can anyone think of a way to do this while retaining the capability of having a default error handling case? The problem is that there can only be one default label in a switch statement. 2. In Ada, the EXCEPTION block is within the scope of variables declared in the BEGIN...EXCEPTION part. This seems like a good idea, because the exception handlers may want to examine these variables to decide how to handle the exception. It doesn't seem possible to do this directly in C++, and it is easy to achieve the same effect simply by writing, for example: { int x; BEGINX // do something to x // do something that might raise an error EXCEPTION case : // use x ENDX } But this raises the question of what happens if x is allocated to a register. And is there a way to force stack allocation of an auto variable in C++? --- Keith Gorlen Computer Systems Laboratory Division of Computer Research and Technology National Institutes of Health Bethesda, MD 20892 phone: (301) 496-5363 uucp: {decvax!}seismo!elsie!cecil!keith #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create the files: # exception.h # exception.c # xtest.c # This archive created: Tue Feb 25 09:14:38 1986 export PATH; PATH=/bin:$PATH echo shar: extracting "'exception.h'" '(1810 characters)' if test -f 'exception.h' then echo shar: will not over-write existing file "'exception.h'" else sed 's/^ X//' << \SHAR_EOF > 'exception.h' X/* exception.h -- Ada -like exception handler X XAuthor: X K. E. Gorlen X Bg. 12A, Rm. 2017 X Computer Systems Laboratory X Division of Computer Research and Technology X National Institutes of Health X Bethesda, Maryland 20892 X Phone: (301) 496-5363 X uucp: {decvax!}seismo!elsie!cecil!keith X February, 1986 X XFunction: X XDeclarations for Ada -like exception handling. X XModification History: X X*/ X X#ifndef EXCEPTIONH X#define EXCEPTIONH X X#include X Xclass ExceptionEnv; Xextern ExceptionEnv* exception_env_stack_top; X Xclass ExceptionEnv { X ExceptionEnv* prev; X int exceptionCode; X jmp_buf env; Xpublic: X ExceptionEnv() { // MUST be inline X prev = exception_env_stack_top; X exception_env_stack_top = this; X exceptionCode = setjmp(env); X } X ~ExceptionEnv() { if (exception_env_stack_top == this) pop(); } X int code() { return exceptionCode; } X void pop() { exception_env_stack_top = prev; } X void raise(int exception); X}; X X#define EXCEPTION_CODE exception_environment.code() X X#define BEGINX { \ X ExceptionEnv exception_environment; \ X if (EXCEPTION_CODE == 0) { \ X X// Statements in the scope of this exception handler block go here. X X#define EXCEPTION \ X } \ X else switch(EXCEPTION_CODE) { \ X X/* XException handlers go here; the syntax is that of a switch statement Xbody. The exception code that caused this EXCEPTION block to be entered Xmay be accessed via the macro EXCEPTION_CODE. The statement X"default:RAISE(EXCEPTION_CODE);" will propagate the current exception up Xto the next exception handler block if the exception is not handled by Xthis block; otherwise, execution continues with the first statement Xafter this exception block. X*/ X X#define ENDX \ X }; \ X} \ X Xinline void RAISE(int exception) X{ X exception_env_stack_top->raise(exception); X} X X#endif SHAR_EOF if test 1810 -ne "`wc -c < 'exception.h'`" then echo shar: error transmitting "'exception.h'" '(should have been 1810 characters)' fi fi # end of overwriting check echo shar: extracting "'exception.c'" '(838 characters)' if test -f 'exception.c' then echo shar: will not over-write existing file "'exception.c'" else sed 's/^ X//' << \SHAR_EOF > 'exception.c' X/* exception.c -- Ada -like exception handler library routines X XAuthor: X K. E. Gorlen X Bg. 12A, Rm. 2017 X Computer Systems Laboratory X Division of Computer Research and Technology X National Institutes of Health X Bethesda, Maryland 20892 X Phone: (301) 496-5363 X uucp: {decvax!}seismo!elsie!cecil!keith X February, 1986 X XFunction: X XRun-time support for Ada -like exception handling. X XModification History: X X*/ X#include X#include X#include X#include "exception.h" X XExceptionEnv* exception_env_stack_top; XExceptionEnv lastResort; X Xvoid ExceptionEnv::raise(int exception) X{ X if (exception == 0) { X cerr << "Tried to RAISE exception code 0\n"; X abort(); X } X if (prev == 0) { // i.e., this == &lastResort X cerr << "Unhandled exception code " << exception << "\n"; X exit(1); X } X pop(); X longjmp(env,exception); X} SHAR_EOF if test 838 -ne "`wc -c < 'exception.c'`" then echo shar: error transmitting "'exception.c'" '(should have been 838 characters)' fi fi # end of overwriting check echo shar: extracting "'xtest.c'" '(1298 characters)' if test -f 'xtest.c' then echo shar: will not over-write existing file "'xtest.c'" else sed 's/^ X//' << \SHAR_EOF > 'xtest.c' X#include "exception.h" X#include X Xenum exceptionCode { EXCEPTION1=1, EXCEPTION2, EXCEPTION3, EXCEPTION4 }; X Xvoid x(exceptionCode n) X{ X BEGINX X if (n>EXCEPTION2) { X cerr << "Raising EXCEPTION" << n << "..."; X RAISE(n); X } X cerr << "Trying normal return from function\n"; X return; X EXCEPTION X case EXCEPTION3: cerr << "EXCEPTION3 handled\n"; return; X default: cerr << "trying RAISE(EXCEPTION_CODE)..."; X RAISE(EXCEPTION_CODE); X ENDX X} X Xmain() X{ X cerr << "Begin exception handler test\n"; X X BEGINX X cerr << "Testing normal execution\n"; X EXCEPTION X default: cerr << "This should not happen!\n"; X ENDX X X BEGINX X cerr << "Raising EXCEPTION1..."; X RAISE(EXCEPTION1); X cerr << "EXCEPTION1 not handled!\n"; X EXCEPTION X case EXCEPTION1: cerr << "EXCEPTION1 handled\n"; X ENDX X X BEGINX X cerr << "Testing nested exception block\n"; X x(EXCEPTION2); X BEGINX X cerr << "Raising EXCEPTION2..."; X RAISE(EXCEPTION2); X cerr << "EXCEPTION2 not handled!\n"; X EXCEPTION X case EXCEPTION2: cerr << "EXCEPTION2 handled\n"; X cerr << "Raising EXCEPTION3..."; X x(EXCEPTION3); X ENDX X X cerr << "Raising EXCEPTION4..."; X x(EXCEPTION4); X cerr << "EXCEPTION4 not handled!\n"; X X EXCEPTION X default: cerr << "Test unhandled exception handler\n"; X RAISE(EXCEPTION_CODE); X ENDX X} SHAR_EOF if test 1298 -ne "`wc -c < 'xtest.c'`" then echo shar: error transmitting "'xtest.c'" '(should have been 1298 characters)' fi fi # end of overwriting check # End of shell archive exit 0 -- --- Keith Gorlen Computer Systems Laboratory Division of Computer Research and Technology National Institutes of Health Bethesda, MD 20892 phone: (301) 496-5363 uucp: {decvax!}seismo!elsie!cecil!keith