keith@nih-csl.UUCP (keith gorlen) (02/07/88)
In article <3117@okstate.UUCP>, norman@a.cs.okstate.edu (Norman Graham) writes: > in article , keith@nih-csl.UUCP (keith gorlen) says: > > > My inclination is to ignore the OOPS exception handling code as much > > as possible, and replace it when exception handling is incorporated > > into C++. > > I tried desperately to design a suitable (ie. simple, transparent, etc) > exception handling scheme for a project I'm involed with ( >80000 lines > of C source in >450 files). We wanted to use a stack of exception frames > and we would have, except for one problem... we couldn't guarentee that the > exception frame stack would be poped on each function return. Actually, popping the exception frame on each function return is the easy part. You just make exception frames instances of a class that has a destructor, and have the destructor update the exception frame stack. But don't get excited -- the real problem is that when an exception occurs and you want to jump out to an enclosing exception frame, there's no way to get the destructors of all the objects local to blocks that are going out of scope called without help from the compiler. Bjarne has mentioned in several papers and talks that he's working on exception handling for C++. An additional problem is that the C++ exception handling mechanism should work in situations where you have C and C++ functions calling one another, and the C functions don't understand C++ exception handling. -- 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
shapiro@blueberry.inria.fr (Marc Shapiro) (03/04/88)
In article <306@nih-csl.UUCP> keith@nih-csl.UUCP (keith gorlen) writes: >But don't get excited -- the real problem is that when an exception >occurs and you want to jump out to an enclosing exception frame, >there's no way to get the destructors of all the objects local to >blocks that are going out of scope called without help from the >compiler. Well, it can be fudged. We have a macro package for exceptions (thanks to Dima Abrossimov) which does the right thing. This is how it works: macro-expand a "raise exception" into "fudge the stack; return". Cfront sees the return and generates calls to destructors of local variables; however you have fudged the stack so that the return jumps into your exception handler instead of continuing in sequence. I'm not saying it's the right way. Proper compiler support is a much better idea. Marc Shapiro INRIA, B.P. 105, 78153 Le Chesnay Cedex, France. Tel.: +33 (1) 39-63-53-25 e-mail: shapiro@inria.inria.fr or: ...!mcvax!inria!shapiro Marc Shapiro INRIA, B.P. 105, 78153 Le Chesnay Cedex, France. Tel.: +33 (1) 39-63-53-25 e-mail: shapiro@inria.inria.fr or: ...!mcvax!inria!shapiro
scs@itivax.UUCP (Steve C. Simmons) (10/10/88)
In article <4370@polya.Stanford.EDU> shap@polya.Stanford.EDU (Jonathan S. Shapiro) writes: >[[an excellent summary of useing overloaded new to implement > garbage collection in exception processing. Heavily summarized:]] >Exception handlers define a scope. >Think of the heap as a temporally scoped object, where the >instantiation of a "catch" scope logically partitions the heap into >stuff older than that catch and stuff newer than that catch. >Now overload all instances of New() within that scope so that the >run-time system maintains, along with the scope information, a spare >pointer to every object allocated with New() within that scope, along >with a pointer to the destructor function. Thus, associated with each >heap scope we have a list of objects and their destructors. > >Now, when an error is raised or a "throw" is executed, we locate the >scope that is thrown to and destroy all of the stack-instantiated >objects. We then destroy dynamically allocated things in the clobbered >scopes in an undefined order. Deallocating the stack-instantiated objects (whether alloca or auto) should be no issue. Heap cleanup is the hard part. >Now I know that there are lots and lots of problems here. I also know >that I don't know a lot about exception handling. Can someone give me >a non-flaming list of problems with this idea so that I can think on it >further? The big problem I see is that there is no good ordering >constraint on the destructions. It's not clear why order is a problem. Could you explain? As for other problems: I've seen PASCALs with an operator that marks the current size of the heap. You can later call a reset operator that resets the heap to that size. This reclaims all the allocated space, but does not restore the structure of the unreset heap. The problem comes when you get complex lists. Half the list was built before the mark, half afterwards. You're willing to give up the data newed after the mark, but your old data is now possibly corrupt -- it may have pointers into the area you just freed. How do you fix it? At least in the PASCALs I've seen, you don't. Now that I think about it, current C programs have the same problem with malloc/free, don't they? Might it be possible to create reference counts to items created with new()? It would add some expense to the new function (tho not a lot), but possibly a lot of expense to the rest of the compiler to maintain those. It would remove any problems I can think of with order of destruction, and garbage collection would start to look suspiciously like fsck. -- Steve Simmons ...!umix!itivax!scs Industrial Technology Institute, Ann Arbor, MI. "You can't get here from here."
shap@polya.Stanford.EDU (Jonathan S. Shapiro) (10/11/88)
In article <301@itivax.UUCP> scs@itivax.UUCP (Steve C. Simmons) writes: >In article <4370@polya.Stanford.EDU> shap@polya.Stanford.EDU (Jonathan S. Shapiro) writes: >>... The big problem I see is that there is no good ordering >>constraint on the destructions. > >It's not clear why order is a problem. Could you explain? The reason ordering is important is that there is a metasemantics to object creation order. Consider a linked list. Destroying the head might be implemented to destroy the rest, but I might be referring to that object someplace else. Consider the following problem that this might lead me to, which I see as the really hard problem here: I instantiate an exception scope, then I create an object (call it Counter) that I am going to use as a counter for the number of Foo objects I have. The Foo constructor appropriately increments the count, the destructor decrements it. If I blow away Counter before I blow away all the Foos I could (potentially) get into trouble (though it will be hard to notice, since the decrement operators are now happening on logically freed memory). The example is contrived. Now imagine that Counter is some object that keeps track of the number of references to itself and deletes itself when appropriate. Such objects are very useful, and could get one into serious trouble here. This example is all too real. Now the metasemantics of the ordering is not readily expressible in the language. How should this problem be addressed? Jon
scs@itivax.UUCP (Steve C. Simmons) (10/11/88)
In article <4383@polya.Stanford.EDU> shap@polya.Stanford.EDU (Jonathan S. Shapiro) writes: [[a nice explaination to my question about why destructor ordering is important in garbage collection scope/exception handling]] >The reason ordering is important is that there is a metasemantics to >object creation order. Consider a linked list. Destroying the head >might be implemented to destroy the rest, but I might be referring to >that object someplace else. >.... >Now the metasemantics of the ordering is not readily expressible in >the language. How should this problem be addressed? Perhaps our problem is that we are failing to properly treat collections of objects. If an object is contained within a larger object, this is something we need to consider when defining the constructor/destructor for both of the objects. Taking an example: We have a class string which contains a reference count. When refcount is zero, it calls its destructor. We have a class stringlist which is a linked list of strings. Should the use of a string in the linked list comprise a reference to it? If you say yes, then as long as the string is on the list it will never be released. If you say no, we have to deal with the case of a list containing a destroyed object. In either case, it's something we *should* be accounting for in designing the list object. Should the list object should be smart about the objects it contains, and be able to remove items from the list when the list is the only referent? Clumsy, but maybe appropriate for some objects. More general is a list object that notices when something it references has been deallocated and cleans up the list. But just how do we detect that in a reliable fashion? Hmm. Now we've got an example where the cleanup problems are independant of exception processing. Could we be so bold to say that there are no cleanup problems that could not arise independant of exception processing? I dunno. This is a very slipperly slope we're talking about. Pretty much everything I can think of seems to seriously bend the rules of data hiding. Sigh. -- Steve Simmons ...!umix!itivax!scs Industrial Technology Institute, Ann Arbor, MI. "You can't get here from here."
ttwang@polyslo.CalPoly.EDU (Thomas Wang) (03/01/89)
I posted because the mail bounced. I am a CSC student planing on doing a thesis on C++ exception handling. What is a rough estimate of the time when C++ would have exception handling build-in? Also in what syntax form would it appear in? Thank you much. -Thomas Wang ("I am, therefore I am." - Akira ) ttwang@polyslo.calpoly.edu
davidm@uunet.UU.NET (David S. Masterson) (09/22/90)
Can somebody provide an example of using exception handling (as defined in E&S) with constructor failures? I think I understand the definitions, but a piece of annotated, example code would certainly help. Thanx -- ==================================================================== David Masterson Consilium, Inc. uunet!cimshop!davidm Mtn. View, CA 94043 ==================================================================== "If someone thinks they know what I said, then I didn't say it!"
mjv@objects.mv.com (Michael J. Vilot) (10/03/90)
David Masterson asked for an example of exception handling with constructor failures: int f() throw(X); int g() throw(Y); class A { int a_part; public: A() { a_part = f(); } ... }; class B : public A { int b_part; public: B() /* A() invoked */ { b_part = g(); } ... }; main() { try { B b; } catch(X) { // we get here if f() failed while building b.a_part // b.b_part not attempted } catch(Y) { // we get here if g() failed while building b.b_part // note that if `a_part' had been of a class with a // destructor, then that destructor would have been executed } } [My aplogies to everyone -- my earlier posting re-sent his question, instead of my response.] -- Mike Vilot, ObjectWare Inc, Nashua NH mjv@objects.mv.com (UUCP: ...!decvax!zinn!objects!mjv)
davidm@uunet.UU.NET (David S. Masterson) (10/06/90)
In article <958@zinn.MV.COM> mjv@objects.mv.com (Michael J. Vilot) writes: [I] asked for an example of exception handling with constructor failures: int f() throw(X); int g() throw(Y); class A { int a_part; public: A() { a_part = f(); } ... }; class B : public A { int b_part; public: B() /* A() invoked */ { b_part = g(); } ... }; main() { try { B b; } catch(X) { // we get here if f() failed while building b.a_part // b.b_part not attempted } catch(Y) { // we get here if g() failed while building b.b_part // note that if `a_part' had been of a class with a // destructor, then that destructor would have been executed } } Would it be valid to add a call to another function (say myf()) at the end of main() where its defined as below? int myf() { try { B b; } catch(X) { // something different than previous catch(X) } catch(Y) { // something different than previous catch(Y) } } Point is, is it valid to reset how exceptions are processed? -- ==================================================================== David Masterson Consilium, Inc. uunet!cimshop!davidm Mtn. View, CA 94043 ==================================================================== "If someone thinks they know what I said, then I didn't say it!"
mjv@objects.mv.com (Michael J. Vilot) (10/11/90)
David Masterson asks: > Would it be valid to add a call to another function (say myf()) at the end of > main() ... Point is, is it valid to reset how exceptions are processed? The short answer is: Yes. Handlers for the same exception attached to different blocks are distinct. So, a handler for catching X's thrown from one block need not do the same thing as a handler for catching them in some other context. Note, however, two points: 1. For the set of handlers attached to the _same_ block, ``control is transferred to the nearest handler of an appropriate type ... the handlers for a try-block are tried in order of appearance. It is an error to place a handler for a base class ahead of a handler for its derived class since that would ensure that the handler for the derived class would never be invoked.'' [E&S, Ch 15] 2. ``An exception is considered handled upon entry to a handler.'' That is, it takes an explicit action to throw (or re-throw) in order to propagate an exception beyond a matching handler. Hope this helps, -- Mike Vilot, ObjectWare Inc, Nashua NH mjv@objects.mv.com (UUCP: ...!decvax!zinn!objects!mjv)
sdm@cs.brown.edu (Scott Meyers) (01/23/91)
I'm trying to understand the proposed exception handling mechanism, and since I don't have access to an implementation, I have a couple of questions about exceptions, especially as regards constructors. Consider: class NoMoreMemory { ... }; // one class of exceptions class OtherException { ... }; // another class of exceptions // a class where the constructor may raise exceptions class Foo { public: Foo() throw (NoMoreMemeory, OtherException); }; // a global object whose construction may raise exceptions Foo globalFoo; // a function with local objects that may raise exceptions void f() { Foo local1; try { Foo local2; } catch (NoMoreMemory) { /* do something */ } catch (OtherException) { /* do something else */ } } Is it true that: 1. There is no way to catch exceptions raised during the construction of globalFoo except via calls to set_unexpected? 2. f cannot catch exceptions raised during the construction of local1 because that declaration isn't in a try block? 3. Any function can be declared to raise an exception, including operators new and delete and class destructors? 4. Assuming that it is legal for destructors to raise exceptions, you may get different behavior from such a destructor depending on whether it is called "normally" or as part of the stack unwinding process during the handling of another exception (ARM 15.6.1c)? Also, speaking of implementations, what's the state of g++ exception handling these days -- does it implement something akin to that described in chapter 15 of the ARM? Thanks, Scott ------------------------------------------------------------------------------- What do you say to a convicted felon in Providence? "Hello, Mr. Mayor."