Path: utzoo!utgpu!news-server.csri.toronto.edu!rutgers!cs.utexas.edu!uunet!brunix!drc From: drc@cs.brown.edu (David R. Chase) Newsgroups: comp.lang.c++ Subject: Re: Exception Handling in C++ Message-ID: <36426@brunix.UUCP> Date: 14 Apr 90 21:44:19 GMT References: <680@mead.UUCP> <1990Apr13.034540.10997@brutus.cs.uiuc.edu> <1990Apr13.192428.17008@brutus.cs.uiuc.edu> Sender: news@brunix.UUCP Reply-To: drc@cs.brown.edu (David R. Chase) Distribution: usa Organization: Sun Lines: 77 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 "" and "". 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