dianne@mead.UUCP (Dianne Glickfield) (04/11/90)
Hello,
I was wondering if anyone could give me some information on the exception
handling design that Bjarne Stroustrup and Andrew Koening presentend and the
" C++ at Work " conference in November 1989. I would be interested in hearing
what was said. Email or posting is fine. Thanks in advance.
_______
Dianne Steele Glickfield Product Delivery Systems
Mead Data Central dianne@uccba.uc.edu
P.O. Box 933 ...!uccba!mead!koala!dianne
Dayton, OH 45401 (513) 865-1293zweig@cassius.cs.uiuc.edu (Johnny Zweig) (04/13/90)
dianne@mead.UUCP (Dianne Glickfield) writes: >Hello, > I was wondering if anyone could give me some information on the exception >handling design that Bjarne Stroustrup and Andrew Koening presentend and the >" C++ at Work " conference in November 1989. I would be interested in hearing >what was said. Email or posting is fine. Thanks in advance. >_______ >Dianne Steele Glickfield Product Delivery Systems Bjarne gave a talk about the revised/ANSIfied version of this at the USENIX C++ Conference in San Francisco the day before yesterday. Michael Tiemann told us that G++ 1.36 has a similar mechanism (and he seemed to say that he would probably change it to match Bjarne's specification in the new paper, though he was unclear on the point). The basic idea is to introduce blocks with exception-handlers like: try { //some code which might say "throw Foo();" or "throw Bar();" } catch(Foo f) { //f is an object of type Foo which is the exception -- it's //constructor could take arguments so the exception can tell you stuff } catch(Bar b){ //ditto } There is also a way that a function can declare what exceptions it might raise to allow type-safe exception handling (whether functions that lack a throw-clause are saying they can generate any exception or no exception is unclear to me; it makes a big difference in what you can do and there is a nice religious safety vs. flexibility argument associated with which it is -- I'll read the paper and post which it is if noone else comments). So wait until your local library gets a copy of the proceedings and all will be revealed. -Johnny Exceptional
zweig@brutus.cs.uiuc.edu (Johnny Zweig) (04/14/90)
zweig@cassius.cs.uiuc.edu (Johnny Zweig) writes: >.... >There is also a way that a function can declare what exceptions it might >raise to allow type-safe exception handling (whether functions that lack >a throw-clause are saying they can generate any exception or no exception >is unclear to me; it makes a big difference in what you can do and there >is a nice religious safety vs. flexibility argument associated with >which it is -- I'll read the paper and post which it is if noone else >comments). Because we want C++ programs to be able to call C functions and to keep the programmer from having to scribble all over everything to have an exception be passable through all the levels of indirection (i.e. have main() catch some scary exception that is raised by some low-level math routine) the decision was made that undecorated functions can throw any exception. Please note that the syntax given in the paper was changed to that shown above (the paper says catch(f) raises f, rather than throw()...). An errata sheet was handed out at the conference but I doubt the proceedings will be corrected before copies go out to your local library. Also, the paper goes into some gibberish (no offense intended to Mr. Stroustrup, but this section was smoking something) about whether an exception is an object or a class. An exception is an object and a handler takes a (possibly anonymous) exception as an argument. The fact that I can say "catch(Foo){" to catch any object of type Foo (or a subtype of Foo) and be unable to refer to it is syntactically odd but not tricky to think about. The paper talks about being able to raise a pointer to an object as if it were something scary -- I think it looks like function-calling and I always think of functions as taking objects (though pointers aren't objects in C++...sigh!). I would welcome anyone who can explain this to me. It seems to be because you can't get the right flavor of virtual function when you pass an object (as opposed to a pointer to an object) -- so you can pass an object or a pointer to a new object (by saying "throw(new Foo);") but it is totally ganjafied to say it is both an object and a class. -Johnny Exceptional
horstman@sjsumcs.sjsu.edu (Cay Horstmann) (04/14/90)
In article <1990Apr13.034540.10997@brutus.cs.uiuc.edu> zweig@cs.uiuc.edu writes: > >Bjarne gave a talk about the revised/ANSIfied version of this at the >USENIX C++ Conference in San Francisco the day before yesterday. >The basic idea is to introduce blocks with exception-handlers like: > >try { > //some code which might say "throw Foo();" or "throw Bar();" >} >catch(Foo f) { > //f is an object of type Foo which is the exception -- it's > //constructor could take arguments so the exception can tell you stuff >} ... >So wait until your local library gets a copy of the proceedings and all >will be revealed. > Actually, the paper uses the SAME keyword for throwing and catching: catch( f() ) throws--see the difference between it and catch( Foo f )? I am all for keyword saving, but this one is too much. (Well, we should be grateful he didn't use "virtual" or "static"--the all-purpose keywords of the 90s.) In his talk he did use "throw" and "catch". What is wrong with "raise" and "handle" ? Not catchy enough? Cay P.S. The keyword "try" is not really necessary. One could take the position (like some languages do) that each block can have its own "handle" clause handling any errors encountered in that block.
drc@cs.brown.edu (David R. Chase) (04/15/90)
In article <1990Apr13.192428.17008@brutus.cs.uiuc.edu> zweig@cs.uiuc.edu writes: >>(whether functions that lack >>a throw-clause are saying they can generate any exception or no exception >>is unclear to me; it makes a big difference in what you can do and there >>is a nice religious safety vs. flexibility argument associated with >>which it is -- I'll read the paper and post which it is if noone else >>comments). > >Because we want C++ programs to be able to call C functions and to keep >the programmer from having to scribble all over everything to have an >exception be passable through all the levels of indirection (i.e. have main() >catch some scary exception that is raised by some low-level math routine) the >decision was made that undecorated functions can throw any exception. That's too bad. Unfortunately, other languages do it the same wrong way, so it's not clear how much flexibility you sacrifice with default = "generates no exceptions". Mick Jordan and Steve Glassman studied this at length for Modula-2+ and Modula-3, and decided that "raises none" ought to be the default. They studied a large collection of Modula-2+ programs (a compiler, and operating system -- that sort of large collection) and discovered that (1) they found several latent bugs by changing the default and (2) they only had to change a few procedure declarations to make the code compile again. Now, as far as compatibility with C goes -- that's a bit of a problem. There's no difficulty at all in the implementation, but you'd have to introduce some sort of annotation in the declarations for all the non-C++ procedures. It shouldn't be necessary to change all the C code, if exceptions can propagate through several callers. (Stroustrop got that right, didn't he? Life is much simpler if the exceptions just propagate on out until they find a catcher.) Now, as to the usefulness of "catching things in main()" -- you'll have to come up with a better example than that. Speaking from experience (*) schemes that try to combine both exception-handling and return codes get a little baroque -- IF people have exception-handling, then they are apt to use it to signal things like end-of-file, no-file-found, etc., and pretty soon your C++ (**) code is completely useless if called from C (***) because the caller will not see the exception. In practice, it's either C on the outside calling something else, or something-else on the outside calling C. It's possible to write "wrapper procedures" that turn exceptions into return codes, or return codes into exceptions, but there's really no reason to expect that programmers will be any more enthusiastic about this inter-language busy-work than they are about any other inter-language busy-work (****). * experience = porting the Acorn "C And Modula Execution Library" ** or Modula-2+, or Modula-3, as the case may be *** or Fortran, or Pascal **** For example -- "what if someone wanted to use a garbage collector? What does that do to my pointer arithmetic?" Exception subtyping is a good thing, especially if you plan to build extensible packages. Without subtyping, the author of the extensible package cannot anticipate the exceptions that might be raised by refinements (subclasses, e.g.) of her code. In that situation, only "RAISES any" will provide the necessary flexibility. With exception subtyping, the programmer can name the specific interesting exceptions that might be raised, as well as the general classes that are expected in the future, without declaring that ANY exception might be raised. This also allows programmers to write code that is more robust in the face of future changes -- a client might choose to catch (say) "divide-by-zero", "end-of-file" and "<the rest of the math errors>" and "<the rest of the IO errors>". Future weird math errors (whatever they might be) will be treated as math errors in the future, even though we don't know what they are now, and future weird IO errors will be treated as IO errors in the future. Other, truly unanticipated errors will be treated like the disasters that they are. In summary, a default of "RAISES anything" gives the compiler fewer opportunities to catch bugs, but it does provide more flexibility, but the flexibility is almost useless. Sub and super-typing of exceptions provides the only flexibility that is really missing if "RAISES any" is not used. David Chase