Path: utzoo!mnetor!uunet!husc6!cmcl2!nrl-cmf!ames!pasteur!ucbvax!hplabs!hp-pcd!hplsla!jima From: jima@hplsla.HP.COM ( Jim Adcock) Newsgroups: comp.lang.c++ Subject: Re: Proposal for Exceptions for C++ Message-ID: <6590033@hplsla.HP.COM> Date: 11 Apr 88 20:35:00 GMT References: <8180006@eecs.nwu.edu> Organization: HP Lake Stevens, WA Lines: 160 || [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.