djones@megatest.UUCP (Dave Jones) (12/25/87)
In THE BOOK, on page 93, it says, "For historical reasons, *new* simply returns the pointer 0 when it cannot find enough store and no *_new_handler* has been specified." What are the historical reasons? The history of C++ is, after all, rather short. That feature of C has always been a pain in the wazzoo. And the *_new_handler* feature provides a dandy way to keep going when core is depleted, if that's what you have to do. You might have to use a setjmp(), but you're up to the challenge, right? Can anyone give me a good reason why I should not hack *new* so that it prints an error message and terminates the program when core is exhausted and there is no *_new_handler*? Is there, or is there likely to be, code out there which DEPENDS on *new* returning 0? Don't worry about new releases of C++. I can handle that easily enough. I can think of one very, very good reason for *new* NOT to return 0, EVER: library classes, A.K.A. "utilities", A.K.A. "general purpose classes". One of the most extensively used classes at our site will be a general- purpose hash-table package, which deletes buffers and allocates bigger ones as necessary. But any utility class which allocates memory will serve as an illustration. Let's suppose I leave *new* as it is. I have a choice: 1. I can try to inforce the convention that we put checks for NULL into every utility which allocates memory. And insist that any software we purchase also do so. 2. I can try to inforce the convention that we require every utility which allocates memory to set and then restore the *_new_handler*. 3. I can try to inforce the convention that each utility set and restore the new_handler if and only if it is not set already. 4. I can allow memory depletion to be handled by the SIGSEGV handler. Each of these has a serious draw-back. The first requires that each utility have a convention for telling the caller when something has gone wrong. It clutters and complicates the code for both the utility and the caller. The second also clutters the code, and short-circuits any _new_handler the caller might have set, perhaps causing the program not to behave as intended when heap is empty. The calling program might have set a handler which does garbage-collection. Number 3: Best try so far. But still error-prone. What if somebody doesn't know? Or just forgets? At best it makes work for utility-writers. The fourth is what would happen occasionally anyway, in practice, and is the stuff that all night sessions on Pepsi and twinkies are made of. I think I've just convinced myself. Can you show me the error of my ways?
ljz@fxgrp.UUCP (Lloyd Zusman, Master Byte Software) (12/28/87)
In article <185@goofy.megatest.UUCP> djones@megatest.UUCP (Dave Jones) writes: > > >In THE BOOK, on page 93, it says, "For historical reasons, *new* simply >returns the pointer 0 when it cannot find enough store and no *_new_handler* >has been specified." > ... >Can anyone give me a good reason why I should not hack *new* so that >it prints an error message and terminates the program when core is >exhausted and there is no *_new_handler*? Is there, or is there >likely to be, code out there which DEPENDS on *new* returning 0? > ... Please, please, please ... I beg of you, PLEASE don't do that. I used a C compiler under MSDOS a few years ago that did the same thing when malloc() failed: it printed a message and caused the program to exit. We were developing a screen-oriented software package. When our customers would run the code with a lot of the IBM PC's memory taken up with resident code, malloc() would fail often. Since we were using the PC's BIOS system to manipulate the screen, and since the message was written to stderr (or stdout ... I forget), the message would come out either behind a window, where it couldn't be seen, or in big, wierd-looking letters in some random location in the middle of the screen. In either case, the system would have to be reset to some known, "nice" state before anything meaningful could be read from the screen. We finally changed to a different C compiler whose malloc() worked in the conventional manner. This allowed us to check its return value and cause a very graceful exit if the system ran out of memory. We would clear the windows and print out a nice message telling the user that too much resident memory has been taken up on his or her system, and that our software won't run properly unless some of it is removed. Then, the program would exit and leave the screen in a normal, usable state. This kind of control over the user interface is extrememly important when selling software. People don't like to buy software that exits unexpectedly with the machine in a state that requires a reset procedure before anything else can run. People will call and ask for their money back. You need to control the system at all times to avoid these "messy" states. Remember, C++ runs on machines other than unix boxes. For example, there are at least two implementations of it on the IBM PC, where there is no signal handling for things like SIGSEGV, where memory is at a premium and malloc() [ or 'new' ] failures happen as a matter of course, and where an "ungraceful" exit from a program could cause the user to have to reboot the machine. In C++, the programmer could use _new_handler to deal with this kind of problem, but it has a serious drawback: within the _new_handler routine you don't know which 'new' had failed. There are usually several calls to 'new' in a program. Many times, the failure of one 'new' has a different impact on the system than the failure of another. Sometimes I'd like to know exactly which 'new' has failed and take whatever action is appropriate at that time. Hence, sometimes I don't want to use _new_handler so I can check if a specific 'new' has returned a NULL and react accordingly. Perhaps what you could do is provide some sort of default _new_handler that prints a message and exits, but which could be overridden by the programmer setting _new_handler to NULL, in which case 'new' would behave the way it is defined to behave in "the book". But whatever you do, *please* don't protect me from myself. If your C++ implementation is ever for sale, I would not buy it if its 'new' behaved the way you suggested in your posting. ------------------------------------------------------------------------- Lloyd Zusman Master Byte Software Los Gatos, California Internet: fxgrp!ljz@ames.arpa "We take things well in hand." UUCP: ...!ames!fxgrp!ljz
keith@nih-csl.UUCP (keith gorlen) (12/28/87)
In article <191@fxgrp.UUCP>, ljz@fxgrp.UUCP (Lloyd Zusman, Master Byte Software) writes: > In article <185@goofy.megatest.UUCP> djones@megatest.UUCP (Dave Jones) writes: $ $ $ $ $ $In THE BOOK, on page 93, it says, "For historical reasons, *new* simply $ $returns the pointer 0 when it cannot find enough store and no *_new_handler* $ $has been specified." $ $ ... $ $Can anyone give me a good reason why I should not hack *new* so that $ $it prints an error message and terminates the program when core is $ $exhausted and there is no *_new_handler*? Is there, or is there $ $likely to be, code out there which DEPENDS on *new* returning 0? $ $ ... $ $ Please, please, please ... I beg of you, PLEASE don't do that. $ I agree. This sort of problem currently has no good solution in C++ (or in C for that matter). What is needed is an exception handling mechanism like that of BLISS or ADA. Bjarne discusses this issue in his paper "Possible Directions for C++", which is to be published soon in the Proceedings of the First USENIX C++ Workshop. -- Keith Gorlen phone: (301) 496-5363 Building 12A, Room 2017 uucp: uunet!mimsy!elsie!nih-csl!keith National Institutes of Health Bethesda, MD 20892