On Sat, 12 Apr 2008 07:13:25 -0700 (PDT),
is*********@gmail.com
wrote:
>I have a function foo, shown below. Is it a good idea to test each
argument against my assumption? I think it is safer. However, I
notice that people usually don't test the validity of arguments.
For example,
#define MAX_SIZE 100000
int foo(double *x, unsigned ling sz, int option)
nit: ling should be long.
>{
int val;
if (x == NULL) { /* test against NULL */
printf("foo: x cannot be NULL\n");
exit(EXIT_FAILURE);
}
if (sz MAX_SIZE) { /* test against too large sz */
printf("foo: sz=%lu too large\n", sz);
exit(EXIT_FAIULRE);
}
if (option != 0 && option != 1) { /* test against invalid option */
printf("foo: option=%d invalid\n", option);
exit(EXIT_FAILURE);
}
/* do main things */
return val;
}
There are three separate issues here: (A) should you check
arguments, (B) how should you check them, and (C) what should you
do about it if there is an error. Obviously there is quite a bit
of room for variations in style. However here are some
suggestions.
(A) Should you test the validity of the arguments? In general,
the answer is yes. If you do not, an unexpected faulty argument
will produce a mystery bug. These kind of bugs can be doubly
hard to find because (1) you "know" the argument is okay, and (2)
the invalid argument violates the implicit assumptions in the
code.
That said, there are situations where it is reasonable to omit
checks. This can happen when the function is an internal
function within a controlled scope where callers guarantee the
validity of the arguments.
An alternative to checking arguments is to write the code so that
all arguments are valid. Valid may merely mean reporting an
error back to the caller.
(B) How should you test them? In my opinion, the obvious way to
do it is also the worst way to do it if one is programming on any
scale. The obvious way has the form
if (some_condition) {some_action}
where some_condition is a failure condition, and some_action
typically consists of printing an error message and exiting with
EXIT_FAILURE.
The first thing that is wrong with this kind of code is that it
almost never gets tested adequately. (You, dear reader, always
test your code thoroughly but the TOG, the other guy, doesn't.)
The second thing (minor but a source of problems) is that
condition is backwards; that is, what one should be doing is what
assert does - specify what should be true of the arguments.
Assert is the obvious (and useful) choice for code in a test mode
or for code that is never going to see the light of day outside
of your personal environment. In serious code, however, assert
is inadequate. My choice is to roll my own that is coupled with
an error handler.
(C) What should you do about it if there is an error? There are
a number of things wrong with writing your own action code for
each test: (1) In the nature of things the action code is likely
to be untested; (2) Often the function is the wrong place to
decide what to do; (3) It pre-empts having a coordinated error
management policy.
My policy for programs of any size is to write an error handler
as a program wide utility. The error handler takes care of
writing error reports to an error log file. An error report
includes information about the specific fault and system state
information. The response to the error that it takes depends on
options passed to it. Thus, the default action might simply be
to write an error report and exit. However one could have the
option to pass it a callback function which takes corrective
action and continue. If one creates a my_assert macro, another
option might be to have the function return with an error
indicator to the calling function. Usw.
Your specific strategy might be quite different from mine.
However the main point I am making is that you should have a
coherent error management strategy that is robust and works for
you.
Richard Harter,
cr*@tiac.net http://home.tiac.net/~cri,
http://www.varinoma.com
Save the Earth now!!
It's the only planet with chocolate.