Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Posting-Version: version B 2.10.2 9/17/84; site think.ARPA Path: utzoo!watmath!clyde!burl!ulysses!bellcore!decvax!genrad!mit-eddie!think!rose From: rose@think.ARPA (John Rose) Newsgroups: net.lang.c++ Subject: Re: Exception handling in C++ Message-ID: <4463@think.ARPA> Date: Mon, 3-Mar-86 10:13:35 EST Article-I.D.: think.4463 Posted: Mon Mar 3 10:13:35 1986 Date-Received: Wed, 5-Mar-86 03:42:04 EST References: <51@cecil.UUCP> <4431@think.ARPA> <56@cecil.UUCP> Reply-To: rose@think.UUCP (John Rose) Organization: Thinking Machines, Cambridge, MA Lines: 75 In article <56@cecil.UUCP> keith@cecil.UUCP (keith gorlen) writes: >A couple of comments/questions on guaranteeing de-initialization of objects in C++ >when longjmp is used: >1) To use the example of class open_file, what if one writes > { open_file f1("foo"); > open_file f2("bar"); ... } >and the block is exited normally; i.e., not by means of longjmp. Does (should) >C++ guarantee that the destructors will be called in the opposite order that >the constructors were called? "Does": I can only find such a guarantee for static objects (C++ book, p. 158). "Should": Since a declaration/initialiation is guaranteed to be executed after textually preceding decls, it may in general depend on the consistency those preceding objects. It would be a mistake to allow destruction of those objects before the object which relies on them is destroyed. In every other case in C++, initialization and destruction nest properly (statics, base/derived orderings; member object ordering "undefined"). Bjarne, are you silent about auto objects to allow implementations room for some efficiency hack? >2) There is also a problem if one does > open_file* f1 = new open_file("foo"); Yes, allocating catch blocks on the heap has little meaning at best. As you say, they should not be added to the dynamic context. >3) Please explain why spring() must be first in struct: > class open_file { > friend file_catch; > class file_catch : catch { >==> void spring() // depends on being 1st in struct: > { fclose(((open_file *)this)->fp); } > } cb; > FILE* fp; Oops, an unclear comment. The "file_catch" object must be the first member fo the "open_file" object, because of the cast hackery by which the self-pointer of the member object is converted to the self-pointer of the parent object. > { CriticalRegion foo(some_semaphore); ... } Lovely. And why not allow "foo" to be omitted? (Well, that's already defined, and has the wrong scoping.) Now, the only thing left to ensure is that the block can have multiple exits. Have you seen Unix* code like this? for (;;) { int s = spl6(); // Critical section... if (time_to_break()) { splx(s); break; } // More stuff. splx(s); // Do something noncritical. } (E.g., the timeout routine in 4.2.) The dual cleanups are an eyesore, confusing, and bug-prone. How about: for (;;) { { CriticalRegion access_timer_queue(6); // Critical section... if (time_to_break()) break; // More stuff. } // Do something noncritical. } The C++ spec currently disallows the dual construction: Multiple block entries, and the book doesn't say anything about multiple exits either. Since the two constructions are equivalent in implementation difficulty (both require either a switch or duplication of code), I suggest that both be allowed. Imagine how clean a kernel could be if it were written well in C++. (Or C for that matter :-}.) -- ---------------------------------------------------------- John R. Rose, Thinking Machines Corporation, Cambridge, MA 245 First St., Cambridge, MA 02142 (617) 876-1111 X270 rose@think.arpa ihnp4!think!rose