Use of assert.h

E

Eric Sosman

Michael said:
And how, in portable C, is this to be achieved with the assert macro?

tardis 105 =>cat foo.c
#include <assert.h>
int main(void) {
#line 42 "We goofed. Call Michael at 800-555-1212."
assert(0);
return 0;
}
tardis 106 =>cc foo.c
tardis 107 =>./a.out
Assertion failed: 0, file We goofed. Call Michael at 800-555-1212., line 42
Abort
tardis 108 =>
 
C

CBFalconer

Michael said:
.... snip ...


And how, in portable C, is this to be achieved with the assert macro?

How about:

assert(p = "Message", logical_statement);

The point is you 'know' it can never be triggered. Then it costs
nothing to leave it in, assuming it is not a performance hog. If
you expect it to ever be triggered, then I agree with you whole
heartedly.
 
C

Christian Bau

Correct, but still not useful. I find it very hard to debug when all
information I have is "My program just crashed. It said 'assertion
something', and then some numbers. No, I didn't write down the numbers."

In production code, the only purpose of an assert is to stop the program
from running when there is trouble. So you leave asserts in if you think
it is better that your program is stopped by an assert then leaving it
running and risking all kinds of horrible things to happen.

That is a judgement call. In some situations the assert will do more
damage; in some cases removing the assert will do more damage. Depends
on what your program does.

(There is another rare possibility: That the assert actually fixes the
problem. I have _once_ seen code where someone wrote "assert (x = 1);"
by accident instead of "assert (x == 1);". The weird thing was that x
was actually zero at that point, which was a bug, and setting it to 1
fixed the bug. Program crashed when asserts were removed).
 
J

Jan Danielsson

Richard Bos wrote:
[---]
Correct, but still not useful. I find it very hard to debug when all
information I have is "My program just crashed. It said 'assertion
something', and then some numbers. No, I didn't write down the numbers."

I have never used assert to actually "debug" (I'm getting into
semantics here). I use assert to identify that a problem has occured,
and thanks to it telling me where the problem occured, I know _where_ to
debug. Asserts have, to me, proved quite unhelpful when I'm working with
someone elses code, but for my own it's always been a nifty tool (since
when they occur, you have a pretty good idea about what could have gone
wrong).

IMHO, if someone is thinking that assert() is about replacing a
debug_print()-routine, then that someone does not understand how to
properly make use of assert().
 
R

Rob Thorpe

CBFalconer said:
But "quis custodes custodiens" <spelling?> applies. You vet your
data, and believe you have caught everything, thus routine foo will
never receive faulty parameters, and checking and correcting them
is not worth while. The assert in foo catches your faulty
thinking, and does no harm if you are correct. If triggered it
should tell the user "We goofed. Call Michael at 800-555-1212 and
tell him that <whatever> happened in foo", not "error 22".

This was exactly what I was thinking about.

I've written a program where data comes in from a set of sources. If
one of a set of values in the program are larger than 10000000 then it
will fail. So, everywhere the data enters it is checked to make sure
it is less than that number.

By the time the values have reached the subroutine where they're used
they cannot be incorrect. But it seems useful to check again at this
stage again, I lose nothing by doing so. If I have in fact checked
the code correctly then nothing is wrong, if I haven't then at least a
moderately useful error message appears to the user. I can't use a
normal error check, because if the subroutine recieves an incorrect
value then everything else in the program screwed anyway, so aborting
makes sense.

So, what seems logical to me is to put something similar to an assert
in. But without disabling it in the users version of the software.
The function I wrote does a little more than assert, to make it more
informative.
 
R

Richard Bos

Emmanuel Delahaye said:
Michael Wojcik wrote on 25/03/05 :

What ? Sounds to be a narrow mind approach, doesn't it ?

It's exactly what the Standard says. Print a message where the end user
probably won't notice it, then abort without a by-your-leave.

Richard
 
R

Richard Bos

Stephen Sprunk said:
An "ordinary user" should never get a copy of your program with assert()s
enabled, so that's a moot point.

No, that is my entire point. assert() is _not_ useful for the finished
product, only for in-house debugging. For anything your user sees (or
even, heaven forbid, uses!), you need a more user-friendly (not to
mention support-desk-friendly) assertion mechanism.

Richard
 
S

Stephen Sprunk

CBFalconer said:
Agreed, but you omit some necessary areas. You simply can't
ensure such 'safe' operation, because the library portions will crash.
You can't use a NULL FILE* to fread, or a NULL char * to strlen,
for example. So you normally attempt to prevent these from arising
early on, by putting the check right at the fopen, or malloc, for
example. However some people get sloppy, and don't install those
checks at first writing. I know it isn't you, but you do have
sloppy apprentices and helpers, don't you? So somewhere along the
line you think you have put all those checks in, and can turn off
the assert. Guess again.

If I put an assert() in my code, then I also put in error handling code
right after it for the same condition. The sloppy coders that come after me
will hopefully learn from that example; the ones that don't wouldn't think
to assert() in the first place.
This is why I try to define as much behaviour as possible in my
routines. For example I have been criticized for having my strlcpy
and strlcat versions interpret NULL input (but not output)
parameters as empty strings. My attitude is if you want to check
against NULL, do so before calling. The routine is intended to
work in production, not to rub your nose in your own misdeeds. The
C library that I use here will, when asked to printf a NULL string,
write out "{null}" instead of crashing. That has been known to
give me a clue as to my own misdeeds.

If I were writing the same functions, I would assert() the arguments aren't
NULL but non-debug versions would do something similar to what you describe.
I'd still document the functions as not accepting NULL if only because
strcpy() and strcat() don't define corresponding behavior.
Crashes are bad. Crashes are better than data destruction.
Crashes with messages and no data destruction are better yet. Data
destruction is especially bad, and this includes recording
erroneous data. Some programmers are fallible (even I).

Agreed. I don't write perfect code, but I find aborting on errors focuses
my debugging better than silently handling them.

S
 
E

E. Robert Tisdale

Rob said:
In general,
is it considered bad practice to use asserts in production code?

What about writing a macro that does the same as assert
but continues to work regardless of the state of NDEBUG?

Suppose that you are writing a function library
for other C programmers to use with a function

double sqrt(double);

for example,
and your Application Program Interface (API) specifies that
it is a programming error to pass a negative value to sqrt(double).
You can help programmers who use your library
to trap and locate this bug:
> cat library.h
#ifndef GUARD_LIBRARY_H
#define GUARD_LIBRARY_H 1
#include<math.h>

inline
double sqrt_error(double x, char* file, int line) {
if (x < 0.0) {
fprintf(stderr, "In file %s: line #%d: "
"argument 1 in function sqrt(double) is negative!",
file, line);
}
return sqrt(x);
}

#ifndef NDEBUG
#define sqrt(x) sqrt_error((x), __FILE__, __LINE__)
#endif//NDEBUG

#endif//GUARD_LIBRARY_H

Unless NDEBUG is defined,
every invocation of sqrt(x) will be replaced
with sqrt_error((x), __FILE__, __LINE__)
and sqrt_error(double, char*, int) will report
the file name and line number where the bug occurred.
 
M

Michael Wojcik

Michael Wojcik wrote on 25/03/05 :

What ? Sounds to be a narrow mind approach, doesn't it ?

Perhaps it is. I didn't design it; I'm merely noting what it was
designed to do.

Perhaps you mean the assert macro is *intended* to be used "only to
trap programming errors". I can't speak to its author's intentions,
and frankly I don't particularly care what they were. We have the
assert macro; it does a certain thing; I don't feel that thing is
useful. It could have been intended to bring peace and joy to the
whole wide world, for all I care - that still doesn't make it useful
in my opinion.

I'll reiterate my position: it does not take significantly more effort
to write a proper error handler than it does to write an assertion.

--
Michael Wojcik (e-mail address removed)

You brung in them two expert birdwatchers ... sayin' it was to keep us from
makin' dern fools of ourselfs ... whereas it's the inherent right of all to
make dern fools of theirselfs ... it ain't a right held by you official types
alone. -- Walt Kelly
 
M

Michael Wojcik

How about:

assert(p = "Message", logical_statement);

The point is you 'know' it can never be triggered. Then it costs
nothing to leave it in, assuming it is not a performance hog. If
you expect it to ever be triggered, then I agree with you whole
heartedly.

But *my* point is that regardless of whether I expect the error
path to be followed (and I never expect any error path is impossible;
transient memory errors do occur, after all), it costs me no more to
put in proper error handling than it would cost me to put in an
assertion.

What does the assertion buy you that proper error handling does not?
The assertion is not generated automatically - C doesn't come with
built-in Design-by-Contract. The programmer either decides to put in
some form of input validation, or decides not to. In the former case
it might as well be something better than assert. In the latter any
usefulness of assert is moot, because there won't be an assert there
either. In neither case is the use of assert justified.

--
Michael Wojcik (e-mail address removed)

Most people believe that anything that is true is true for a reason.
These theorems show that some things are true for no reason at all,
i.e., accidentally, or at random. -- G J Chaitin
 
M

Michael Wojcik

assert( (p = "Message", logical_expression) );

Is the "p =" even necessary? I don't see anything wrong with just
having "Message" stand alone as the LHS of the comma operator. It's
just an expression with no side effects. I guess

"Message" && logical_expression

would work as well, for that matter, and dispense with the extra
parentheses in the bargain. Or

logical_expression? "Message" : 0

I'm sure there are other torturous ways of achieving with the assert
macro something that could be done much more elegantly without it.

Perhaps from now on I'll wrap every single line of code in an assert
macro, using the logical-negation and comma operators to force results
to the right value, so that when an error occurs I'll know just what
line was the problem. Who needs debuggers?
 
C

CBFalconer

Michael said:
But *my* point is that regardless of whether I expect the error
path to be followed (and I never expect any error path is
impossible; transient memory errors do occur, after all), it costs
me no more to put in proper error handling than it would cost me
to put in an assertion.

What does the assertion buy you that proper error handling does
not? The assertion is not generated automatically - C doesn't come
with built-in Design-by-Contract. The programmer either decides
to put in some form of input validation, or decides not to. In
the former case it might as well be something better than assert.
In the latter any usefulness of assert is moot, because there
won't be an assert there either. In neither case is the use of
assert justified.

You have already done the proper error handling (you thought) up
front where you knew how to handle it. This assert is deep in the
bowels, and the routine has no idea what to do next. If it ever
gets triggered you know you goofed somewhere, and you may get a
clue as to where. Sure, it would be preferable to have handled it
properly in the proper place, but you didn't, did you? Meanwhile
it has saved your long-suffering user from the destruction of his
data over the past 20 years. You and Dan Pop may be infallible,
but I suspect that I'm not.
 
K

Keith Thompson

Fair enough, albeit rather pointless. I hope that wasn't what CBF
had in mind.

I wouldn't call it pointless (unless you mean that the message should
have been in the argument to assert(), or to some better error
handling routine), but it does clobber the line number and file name,
which could mess up other diagnostics.
 
C

Christian Bau

But *my* point is that regardless of whether I expect the error
path to be followed (and I never expect any error path is impossible;
transient memory errors do occur, after all), it costs me no more to
put in proper error handling than it would cost me to put in an
assertion.

What does the assertion buy you that proper error handling does not?
The assertion is not generated automatically - C doesn't come with
built-in Design-by-Contract. The programmer either decides to put in
some form of input validation, or decides not to. In the former case
it might as well be something better than assert. In the latter any
usefulness of assert is moot, because there won't be an assert there
either. In neither case is the use of assert justified.

assert should be used to find bugs in your program. Example:

for (i = 0; i < n; ++i) a = 0;
assert (i >= n && (n < 0 || i == n));

After execution of the loop, i will be equal to n, except if n was
negative. There will be no assertion unless I made some mistake in this
tiny bit of code. If I made a mistake, then how could I possibly handle
it? If I knew that there is a bug in the code and which bug, then I
would fix it. But I believe there is no bug, and I have no idea how to
handle it if there was one.

Incorrect inputs are not bugs in the program and therefore should never
be handled by an assert.
 
M

Michael Wojcik

You have already done the proper error handling (you thought) up
front where you knew how to handle it. This assert is deep in the
bowels, and the routine has no idea what to do next.

How many times do I have to say this? I PUT THE ERROR CHECKS IN
THE LOW-LEVEL ROUTINES AS WELL. THEY AREN'T ANY MORE WORK THAN
AN assert WOULD BE.
If it ever
gets triggered you know you goofed somewhere, and you may get a
clue as to where. Sure, it would be preferable to have handled it
properly in the proper place, but you didn't, did you? Meanwhile
it has saved your long-suffering user from the destruction of his
data over the past 20 years. You and Dan Pop may be infallible,
but I suspect that I'm not.

I *know* I'm not, which is why I put the damn error checks in every
function. I have never claimed that it was better to leave all
validation out; I have been claiming that it's better to put in
real error handling than to use assert.
 
M

Michael Wojcik

I wouldn't call it pointless (unless you mean that the message should
have been in the argument to assert(), or to some better error
handling routine),

That is indeed what I meant, since:
but it does clobber the line number and file name,
which could mess up other diagnostics.

makes it an extremely poor choice. It's also fragile, since there's
nothing preventing the programmer from using the same arguments for
#line in another location.

--
Michael Wojcik (e-mail address removed)

There are many definitions of what art is, but what I am convinced art is not
is self-expression. If I have an experience, it is not important because it
is mine. It is important because it's worth writing about for other people,
worth sharing with other people. That is what gives it validity. (Auden)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top