[comp.lang.c++] null news is bad news

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