[snips]
I love how everybody pushes such toy code. You forgot the part inside
{}, you know.
Yes, I did - in *both* cases. Why? Because there's simply not enough
information available to say whether the code in one would be simpler
than in the other, or what that code would be.
In some apps, allocation failure is a critical issue. In others, it's no
more than a minor inconvenience. In others, it's somewhere in between.
The same can be said in the case of file open failures.
Take, oh, a chess program. Can't open it's persistent hash file? Okay,
fine, so it plays a few points weaker than it would otherwise, this is
hardly fatal. Another app, failure to open the file may be fatal.
The absurd notion that just because it's an allocation failure, it is
magically transformed into a world-ending problem which far exceeds the
complexity of a file open problem is, well, absurd.
Not test as in 'if(something)'. Test as in "run the program and see what
happens when that allocation fails".
Oh, well, that's pretty damn easy, too, in many cases. Depending on
environment and implementation, you could replace malloc with one of your
own, or a custom one which allows you to set how much memory to
allocate. You could limit the memory available to a process. The fact
it's an allocation hardly means it's impossible to test.
Yeah. Stack space is also such a resource. Do you always use whatever
your implementation provides to cope with stack allocation failure? Just
curious.
Er, not sure the point here. Last I checked, C had no mechanism
whatsoever to detect available stack space or detect stack failures, and
this is CLC, where we do C. As _C_ code, there is simply no way to
accomplish this task. As _C_ code, there _is_ a way to detect an
allocation failure.
So, how about you explain the strictly conforming methodology for
detecting stack space and detecting stack allocation failures, and we'll
examine whether or not they can be managed in the same sort of way.
Pedantic? Perhaps, but then the code cannot use any particular mechanism
to ensure that the power grid stays up, or that the guy being
communicated with halfway around the world is actually still at his desk,
or that the satellite used to carry the communication signal isn't
merrily falling to earth and about to explode.
These things _cannot_ be checked or handled in C; malloc failures _can_.
So how about we stick to C.
The application has no idea that user clicked "Save" in this case.
How can it _not_? What, was the menu put up there by magic?
So it
didn't fail to save. The very event was dropped and forgotten.
Okay, so it's a bogus UI design, is what you're saying. So, do we shoot
the application designer who designed the UI in such a manner, or is this
yet another glib issue?
Of course
it's a made up example, no need to talk about design here. A more
realistic one is:
void the_toolkit_event_function (Event *event) {
event_queue = g_list_prepend (event_queue, copy (event));
}
What do you do there if g_list_prepend fails because it failed to
allocate space for the list link? You just can't drop the event, because
that could lead to funny things like ignoring user requests (or worse, X
is funny like that).
Not sure, as I don't know what the event mechanism is, what the events
are, what the import is of the whole process. As for "allocate space for
the list link", not sure what that means, either, in that context. Do
you mean that copy() might fail? If so, the code, as written, is - IMO -
very poorly structured, as it makes no provision for copy failing, unless
g_list_prepend is smart enough to detect the NULL and respond back
appropriately, something I wouldn't bet on given the current discussion.
Nor does it tell me enough even if I know this is the case. For example,
it _may_ be perfectly acceptable to simply "spin the wheels" a while,
doing something else, until the copy can be performed. Or it may be
acceptable to simply "block" - wait until space is available. Or there
may be an error reporting mechanism available which allows one to report
and take some other appropriate action.
Nobody said this, you can do stuff on malloc() failure.
Actually, that's pretty much the entire point to both the Malcolm and the
glib sides of the discussion; if allocation fails, abort the app. No ifs
ands or buts, it is allocate or die - which only makes sense if you
*cannot* do anything on allocation failure.
But even without
that, this whole notion is very useful in applications where it's
acceptable. I won't care much if my mp3 player crashes when my X display
is frozen anyway.
If X is frozen, then presumably the mp3 player, which I assume uses X for
a variety of things, is likely to be hosed itself. On the other hand,
perhaps it's not an X mp3 player, but, oh, something like mpg123. Unless
X's freezing is because of total systemic resource exhaustion, why should
an X issue have any impact on the app?
Then again, we're not talking about _X_ freezing here, we're talking
about the notion that an allocation failure means instant death. If my
mp3 app can't handle the size of my playlist - can't allocate space for
it - I expect it to do the sane thing and *tell* me so, not just puke and
die. That's sloppy programming of the worst sort, totally unacceptable,
even in something as trivial as an mp3 player, and absolutely fatal in a
more serious app.
Indeed. And you make these decisions thousands times in your code, and
you test every one, and every one is exactly the best and right. And
there is a peace in the world, and so on.
Correct. Well, apart from the peace in the world part. Yes, allocation
is followed by validation and handling of possible failure. It is not
difficult to do, programmers have been doing it since the invention of
languages with managed memory allocation.
... is especially good when you are writing code. I believe the first
choice is better.
Better how? If the reason I'm allocating the buffer is to improve
performance, the fact I can't get one as large as I want doesn't mean the
program won't work, it simply means it will take a little longer.
Oh, wait, no, that's not right, can't have that, we *must* kill the app,
dead. Can't have anything less than optimal performance, obviously the
app must die.