[comp.std.c] Making C a little more 'foolproof'

martin@mwtech.UUCP (Martin Weitzel) (03/01/90)

In article <12246@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn) writes:
>In article <MCDANIEL.90Feb27103334@amara.amara.uucp> mcdaniel@amara.uucp (Tim McDaniel) writes:
>>Are standard library names utterly and completely reserved, or can I
>>fake it with #define, as in
>>	#define malloc(bytes)	my_malloc(bytes, __FILE__, __LINE__)
>
[correct parts of the answer deleted]
>
>It would be better to use some name other than malloc for this macro.

Yes and No - it depends on why I (or the poster) want to do this.

IMHO, there's a big 'philosophical' difference between languages like
C, which obliges the programmer to do *all* error checking (namely for
"fopen", "malloc" a.s.o., which may only fail under 'unusual conditions')
and PASCAL, which does much more 'holding hands' at the cost, that the
programmer has no chance to regain control, if a FILE cannot be opened,
a NEW cannot make memory available a.s.o.

This often has lead to the general assumption, that C is an 'unsafe'
language, because programmers may tend to ommit error checking
(because they have learned PASCAL earlier than C and think, it's not
necessary, because they think "I'll add it later" and forget to do
so, or because they consider the situation a "never happens" one).

There is much moaning about this 'insecurity of C', heard from novices
(and people, who happen to have an other favorite language than C),
but it's very seldom noticed, that it takes NOT MORE THAN 1 (ONE)
HEADER-FILE and a few additions to the library, to rectify most of it.
(It may be valuable to do this, if you plan to teach courses on C.)

The header file does exactly, what the poster proposes (and I'm
glad ANSI did not make it illegal): You substitute 'unsafe' library
functions, say "foo", by macros of the same name, which call similar
named functions "my_foo" as the original poster proposed, or "sfoo"
as I prefere, where the "s" stands for "safe".

The "s"-functions are defined in a separatly compiled module, and call
the respective original library function (and hence must *not* #include
the header file, which redefines the name). Besides that, they do some
error checking, print diagnostics (for which the hidden arguments __FILE__
and __LINE__ are fine) and may abort the program.

This approach seems most useful for learning C, because the novice can
concentrate to get the syntax and algorithms right, without cluttering
up the progam with code for error checking. (IMHO, this is the reason,
why PASCAL stepped along this road and is - in an unaugmented version -
useless for 'real life' programming, were you often have to undo
some earlier operations, if a later operation is not succesfull.)

But you can make this scheme more sophisticated and usable for
production programming. You only need to call the error handlers from
the "s"-functions via function pointers, that can be altered from outside
(but should default to diagnostic messages and/or termination).

If you extend this idea further (eg keep the error handlers for
each logical level of functions in separate tables and calling
them via double pointers) you may even easily switch tables. The
bottom line is, that you can have much of ADA's or PL/1's exception
handling added to C. It requires no changes to the syntax of C, you
must only make use of those features, which are allready there.
-- 
Martin Weitzel, email: martin@mwtech.UUCP, voice: 49-(0)6151-6 56 83

karl@haddock.ima.isc.com (Karl Heuer) (03/03/90)

In article <681@mwtech.UUCP> martin@mwtech.UUCP (Martin Weitzel) writes:
>In article <12246@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn) writes:
>>It would be better to use some name other than malloc for this macro.
>
>Yes and No - it depends on why I (or the poster) want to do this.
>
>[C requires the user to do error checking; Pascal simply aborts on failure.]
>This often has lead to the general assumption [that C is 'unsafe'],
>but it's very seldom noticed, that it takes NOT MORE THAN 1 (ONE)
>HEADER-FILE and a few additions to the library, to rectify most of it.

I agree that it can be useful to have an error-handling version of malloc(),
but I agree with Doug that it should be called by a different name.
*Especially* if you're teaching a course!  How are your students supposed to
learn that there's a difference between standard malloc() and your local
"improved" version?

Side note: error handling is a botch in ANSI C; this was recognized by the
Committee.  There was no prior art that could be used to build a reasonable
facility, and it was against the Charter to invent one from scratch, so they
kept the system that Unix uses.  As with other such situations, there's room
for it to be fixed in the *next* standardization, if a good system is invented
and tested in the meantime.  With that in mind...

>But you can make this scheme more sophisticated and usable for production
>programming. You only need to call the error handlers from the "s"-functions
>via function pointers, that can be altered from outside (but should default
>to diagnostic messages and/or termination).

Alternately, an implementation could provide these error-handling features in
the actual library routines rather than in "s"-functions.  The default action
would have to be "ignore" (to retain compatibility with the current Standard),
but there could be a simple way for the user to globally change this default.

I don't much like using signal-like semantics, since it often means you have
to use a messy longjmp() to recover.  Another possibility is that when "safe
mode" is enabled, routines continue to indicate errors quietly (i.e. by
setting an internal flag which replaces errno), but only if no error is
already pending.  A routine iserror() would test and clear the internal flag.
Thus, instead of
	if ((vp = malloc(siz)) == NULL) die();
one would write
	vp = malloc(siz);
	if (iserror()) die();
, and instead of
	(void)remove(tmpfile); /* okay if file already gone */
one would write
	remove(tmpfile);
	(void)iserror();
Unlike errno, there would be no risk that the error flag is "old".  It would
never be set by a successful routine, and one may not invoke any "unsafe"
routine (i.e. a function that might set the flag) while it is set.  Doing so
would cause the program to abort with a signal.

Any other ideas?

Karl W. Z. Heuer (karl@ima.ima.isc.com or harvard!ima!karl), The Walking Lint

jv@mh.nl (Johan Vromans) (03/03/90)

In article <681@mwtech.UUCP> martin@mwtech.UUCP (Martin Weitzel) writes:
> IMHO, there's a big 'philosophical' difference between languages like
> C, which obliges the programmer to do *all* error checking (namely for
> "fopen", "malloc" a.s.o., which may only fail under 'unusual conditions')
> and PASCAL, which does much more 'holding hands' at the cost, that the
> programmer has no chance to regain control, if a FILE cannot be opened,
> a NEW cannot make memory available a.s.o.
[ and much more ]

A very elegant approach was taken in the early 70's in Burroughs
Extended Algol. Some operations allowed multiple levels of control,
e.g.:
      READ(INFILE,COUNT,BUFFER)
	-> if something goes wrong, the system would trap it
      READ(INFILE,COUNT,BUFFER)[EOF]
	-> EOF handled by program, others by system
      RESULT := READ(INFILE,COUNT,BUFFER)
	-> complete control by the program

Real powerful language. They don't make them like that these days.

Johan
--
Johan Vromans				       jv@mh.nl via internet backbones
Multihouse Automatisering bv		       uucp: ..!{uunet,hp4nl}!mh.nl!jv
Doesburgweg 7, 2803 PL Gouda, The Netherlands  phone/fax: +31 1820 62944/62500
------------------------ "Arms are made for hugging" -------------------------