assert in C

T

Tim Rentsch

Richard Heathfield said:
Richard said:
And yet many - probably almost all - released programs have errors
that are, or could be, caught by assertions. You may conclude that
most programs are released when they are not ready, but turning off
assertions is not going to fix that.

The only reason to turn off assertions is if their overhead outweighs
their advantage. If their overhead is small, why turn them off at all?
What do you gain?

If you leave assertions on and they never fire in production, you have
a performance cost for no gain. [snip]

Not _no_ gain. If/when you get a bug/crash report from
a program out in the field, if assertions were left on
you know none of them were violated as part of the bug/crash.
Knowing that can be a huge help in reproducing or diagnosing
the problem. Personally I think that's an enormous gain,
but in any case it is some gain.
 
N

Nick

Richard Heathfield said:
Tim said:
If you leave assertions on and they never fire in production, you have
a performance cost for no gain. [snip]

Not _no_ gain. If/when you get a bug/crash report from
a program out in the field, if assertions were left on
you know none of them were violated as part of the bug/crash.
Knowing that can be a huge help in reproducing or diagnosing
the problem. Personally I think that's an enormous gain,
but in any case it is some gain.

Although you haven't convinced me sufficiently to change my habits,
the point you make is a very good one.

I think we've all come to a common position that there are at least two
sorts of assertion - those that cost a lot and should be turned off in
production, and those that are cheap and which probably have some value
in production.

Richard's just about persuaded me that even if I don't have any of the
former in my code, I'm probably better creating my own ALWAYS_ASSERT(x)
macro, even if it just defines to the standard assert, although I'm not
about to put changing it to the top of my planned changes list.
 
E

Ersek, Laszlo

Tim said:
If you leave assertions on and they never fire in production, you have
a performance cost for no gain. [snip]

Not _no_ gain. If/when you get a bug/crash report from
a program out in the field, if assertions were left on
you know none of them were violated as part of the bug/crash.
Knowing that can be a huge help in reproducing or diagnosing
the problem. Personally I think that's an enormous gain,
but in any case it is some gain.

Although you haven't convinced me sufficiently to change my habits, the
point you make is a very good one.

I concur; I never thought of assert()s like this before, even though I'm
a big assert() fan. For example in lbzip2, I placed simple (but in my
opinion, important) assertions even in critical sections, and asked
packagers not to #define NDEBUG for "production" builds. First, I cannot
check the consistency of such a shared structure if it is not locked, so
if I want to verify the struct with an assert() at all, it must happen
while the struct is locked (for the real reason, of course). Second, if
such simple/quick asserts significantly worsened performance, then I'd
be near lock contention, which is a bad thing anyway. (Fortunately, the
granularity of my tasks is huge in comparison to accessing a few
pointers and integers in the critical sections.)

Cheers,
lacos
 
N

Nick

Richard Heathfield said:
Nick wrote:



I'm not sure that it's necessary. The normal assert() macro will do
for expensive, dev-time-only assertions. As for the cheaper kind that
you can leave in, just add error-handling, and then you don't need the
assertion in the first place. :)

It depends on what you mean by "error handling". Let's consider a
string copy function that asserts that neither the destination or the
source are NULL. This isn't a user error, and if you've got to this
state of affairs your programme is doomed. The only reason for the
"assert" is that if your programme dies in assert(src != NULL) it's easier
to know what the problem is than when it dies in *src++ = *dest++.

It also, as I've suggested earlier, is useful documentation (the same as
a comment, but more maintainable) to anyone working on the code that
this function can't cope with NULL arguments.

So rather than spatter the code with
if(src == NULL) {
fprintf(stderr,"Programmer error, sorry!");
exit EXIT_FAILURE;
}

there isn't much I can see that error handling gives you. At the
best, a little macro that:
prints a message (including the __FILE__ and it's friends)
aborts
makes the code a lot tidier. I've now lost track of whether this is my
assert macro or your error handling.
 
S

Seebs

Although you haven't convinced me sufficiently to change my habits, the
point you make is a very good one.

Seconded. I had not considered the curious event of the assertion in
the night-time.

-s
 
R

Richard Tobin

"It's a lemon entry, my dear Watson."

We returned to find dinner waiting for us on the table, prepared by Mrs
Hudson. My attention was immediately caught by a large plate of fish
in a bright yellow citrus sauce. "My God Holmes," I cried, "what is
it?". "A lemon entree, my dear Watson" he replied, looking up from
the sponge and custard dessert which he had been examining minutely
with his lens. "What are you doing?" I asked. "You know my method
Watson, it is founded upon the observation of trifles."

-- Richard
 
E

Eric Sosman

We returned to find dinner waiting for us on the table, prepared by Mrs
Hudson. My attention was immediately caught by a large plate of fish
in a bright yellow citrus sauce. "My God Holmes," I cried, "what is
it?". "A lemon entree, my dear Watson" he replied, looking up from
the sponge and custard dessert which he had been examining minutely
with his lens. "What are you doing?" I asked. "You know my method
Watson, it is founded upon the observation of trifles."

"Come, Watson, come! The game is assert!"
 
F

frank

Richard said:
[Sorry if this is essentially a repeat of an earlier reply - my news
server threw a wobbly.]

Nick wrote:
I've now lost track of whether this is my
assert macro or your error handling.

Well, it isn't my error handling, because my error handling (in library
code) does not terminate the program. The caller probably will, after
doing any necessary cleanup, but the library will not.

That's the part that took a while to sink in with me. There's no
handling a failed assert.
 
N

Nick

Richard Heathfield said:
Well, it isn't my error handling, because my error handling (in
library code) does not terminate the program. The caller probably
will, after doing any necessary cleanup, but the library will not.

That's not the approach taken by the C standard library, though. Given
the paucity of ways of signalling things in C, you are going to end up
with a huge collection of variables propogating things backwards that
only mean "the programmer got it wrong".

Somewhere, there must exist circumstances where there are valid (in C
terms) values for parameters to functions that don't mean anything and
should never be sent.
 
N

Nick Keighley

and ASSERT_ALWAYS() is a cheap and easy way to add error handling.
ditto

One of the basic guiding principles I use in developing library code
(and I think you'll agree that a string-copier is library code) is that
the library must under no circumstances terminate the program

the people who defined the C library didn't follow that rule. If I
strcpy() a NULL pointer it get UB. And from a practical point of view
abort() seems a fine way of "defining" undefined behaviour.
- if it
discovers an error, it must report that error back to the caller. The
decision to be or not to be belongs at the application level.

it does if the caller is breaking the library's preconditions.

In C++ my ASSERT macro throws a logic_error. If the program wants to
it handle the error or shutdown cleanly or whatever.
 
N

Nick Keighley

A paraphrase from one of Holmes's most infamous cases, "The Case of the
Yellow Door".

"It's a lemon entry, my dear Watson."

"just what sort of arboreal oddity is that, Holmes?!"
"it's a lemon tree, my dear Watson."
 
N

Nick Keighley

Right. Perhaps they should have done (and in my view there's no perhaps
about it), but they didn't. Er... so?

I was assuming (perhaps optimisically!) that the writers of the C
library were smart people. The usual argument (I thought you'd made it
in fact...) was that for primitives like strcpy() you want an
unchecked version and the caller makes damn sure he doesn't pass a
NULL (or otherwise invalid) pointer to strcpy(). Um. I suppose that's
an argument for taking the asserts out in the production code.

Obviously we are going round and round here. I like asserting simple
preconditions. And I like to leave such checks in because I'm not
perfect. My tests don't cover every real world case and I'd rather my
programs bailed out than struggle on in an undefined state.
 > If I

And so we learn (slowly) not to strcpy null pointers.

by this time we all know we shouldn't strcpy() null pointers. But it
soemtimes happens anyway. Actually I seem to printf() them more often.
But, from a
programmer's perspective, I would rather have the user send me an error
log that showed me the call stack and therefore the context of the
screw-up rather than, on the other hand, a message just saying "foo.c,
42: assertion (p != NULL) failed".

on many systems abort() triggers a core dump. Ok I don't really use
assert() I use an assert like macro that writes stuff into a log file.
If you call that "error handling" then I handle errors. the code looks
more like this though:-

int process_doggle (Doggle* doggle, int n)
{
ASSERT (doggle_is_valid (doggle));
ASSERT (n != 0);
/* do stuff */
}


that sounds a different strategy from generating a call stack...

Sure. I'd expect the building to be capable of supporting the roof.

fond though I am of civil engineering analogies I think this one is
structurally unsound. We aren't building a building we're writing
software.

If your house is correctly wired you don't need on earth on your plug
(many countries don't have one- where I come from electrical plugs
have three pins).

You shouldn't need fuses or earth leakage detectors but many homes
have one or both of these.
It's
a precondition for the roof that something has to be able to hold it up.
But I'm not going to leave the scaffolding there on the off-chance that
the building suddenly loses structural integrity. In fact, if the
building does lose structural integrity, a falling roof is the last of
your problems. (Literally!) And yes, some buildings /do/ lose structural
integrity, and without scaffolding to hold it up the roof /does/ fall.
But to blame the absence of scaffolding is ridiculous. Buildings are
supposed to be able to stand without scaffolding. And programs are
supposed to be able to stand without assertions. If you daren't take the
assertion out, it's really an error-check.

you should have seen the amount of stuff a customer added under the
false floor to support a cabinet. THEY didn't assume the floor wasn't
going to move about very rapidly at some point.
 
E

Eric Sosman

[...]
Oh, but you do need fuses, because people will insist on sticking metal
forks into toasters. That's a user error, not a design error, and
clearly you need safeguards against it.

You also need fuses when a water pipe bursts and floods
the kitchen floor, causing seepage through the basement ceiling
that dislodges a bit of plaster, which falls unluckily right
into the gubbins-hatch of the furnace's burner motor, jamming it
and causing the motor windings to overheat when the thermostat
clicks over, setting fire to all that nice heating oil and
giving the occupants even more warmth than they'd asked for.
 
N

Nick Keighley

that sounds a different strategy from generating a call stack...

No, not really. For example, you can have something like this (and I'm
just typing this in off the top of my head - it's not supposed to be a
rigorous demo):

mystr *mystr_copy(mystr *dest, mystr *src)
{
   int ok = 0;
   static const char fn[] = "mystr_copy";
   fstack_push(__FILE__,
               __LINE__,
               fn,
               "enter: %p, %p",
               (void *)dest,
               (void *)src);

   if(mystr_is_good(dest) && mystr_is_good(src))
   {
     size_t srcsize = src->size;
     if(mystr_grow(dest, srcsize) == 0)
     {
       memcpy(dest->endptr, src->data, srcsize);
       dest->size += srcsize;
       fstack_pop();
       ok = 1;
     }
   }
   return ok ? dest : NULL;

}

At this level, the fstack contents are going to be somewhat cryptic -
just a function name and some parameter values - but at the application
level they could be more informative: "about to move customer details to
database" or whatever.

interesting. Do you actually do that? It's more work than just putting
a pseudo-assert in and since this is "can't happen" code its therefore
moere likely to be ommitted.
Certainly true, and proof by analogy is flawed - but illustration by
analogy is reasonable.


Sure, but they're cheap to put in, they do increase safety, and they
don't leave random junk in the database.

much like assert then
:)

(a decent ACID database shouldn't be left with random junk just
because you've invoked assert())

<snip>
 

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

Forum statistics

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

Latest Threads

Top