Use of assert.h

R

Rob Thorpe

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?

I can see that it would be poor style to use it for commonly
encountered errors, but what about truly exceptional errors that would
rarely if ever be encountered?
 
F

Flash Gordon

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?

I can see that it would be poor style to use it for commonly
encountered errors, but what about truly exceptional errors that would
rarely if ever be encountered?

The answer is, it depends.

You have to consider for your specific application what is best.
Possibilities include:

1) Abort the program with an appropriate message.
This can prevent incorrect results from being given and so in a
situation where no answer is better than an incorrect answer (or
corrupting a database for example) this might be the best thing.

2) Return an error status to the calling function.
If the calling function does not handle the error this could lead to
incorrect results or a crash.

3) Reboot the system (most likely to be an option on an embedded
system).

4) Attempt to recover the situation and fall back on 1, 2 or 3 if the
correction appears not to work.

For some items such as a video recorder, option 4 falling back to 3
might be the best solution. For one application I am working on option 1
is best for a lot of errors (prevents the data being corrupted and all
the work required to fix the data) but option 4 falling back to 1 is the
best solution for other types of problem.
 
C

Chris Croughton

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

I don't think there is any 'general' opinion, it's largely whatever your
employers or customers think. If you mean "leave them in the code with
NDEBUG defined so that they do nothing" I don't see any problem leaving
them there.
What about writing a macro that does the same as assert but continues
to work regardless of the state of NDEBUG?

If it's in interactive code (by which I mean that the user starts the
application and can see error messages) then I don't think that the
default C assert mechanism is normally appropriate. If I'm using a word
processor, for example, I don't want it to exit with a bit of source
code on a command line and/or a core dump, it would be a lot better to
catch the error and display a meaningful message to the user (and
hopefully correct the error at least enough to do an orderly closedown
of any open files).

In non-interactive code (like embedded systems) the correct response may
well be to reboot or halt the system, possibly after writing the error
message into an area from which it can be recovered later. That may
well be all you can do.
I can see that it would be poor style to use it for commonly
encountered errors, but what about truly exceptional errors that would
rarely if ever be encountered?

It depends on the severity of the error. In some applications I've
worked on there have been a set of 'assert' type macros with differing
degrees of severity and actions depending on whether it was compiled as
debug or production. For instance:

Output a message to the user and continue
Output a message to a local file and continue
Output to the system log and continue
Output to the console and continue
Any of those and abort with a core dump
Any of those and abort with no core dump
Abort without trying to output anything because that will fail
Reboot the system

If you're working in a team those things should already be laid down as
standard procedure, if you're starting out then the project designer
should make those sorts of decisions.

Chris C
 
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?

I can see that
it would be poor style to use it for commonly encountered errors,
but what about truly exceptional errors
that would rarely if ever be encountered?

You are confused.

The assert macro is designed to trap programming errors (bugs).
After you have finished developing, testing and debugging
your application program, you simply define NDEBUG
and recompile your application before releasing the "production code".
Don't edit your code to remove invocations of assert
because that just provides an opportunity to introduce new bugs.

Diagnostics from assert macros are of no use to users
who may not be programmers themselves.
If you release code without deactivating assert macros,
it means that you are still debugging the code
and that you expect users to assist you in testing your code.
You probably shouldn't call such code "production code".

Never use the assert macro to trap exceptions (what you call errors).
Exceptions are expected but unpredictable events
which cannot be prevented but must be handled when they occur.
By contrast, bugs are unexpected but predictable errors
(once they have been discovered)
that cannot be "handled" except by fixing the bug
and then they are prevented from ever occurring again.

There are no "macros" to trap exceptions.
You must write ad hoc code to detect and handle exceptions.
 
E

Emmanuel Delahaye

Rob Thorpe wrote on 23/03/05 :
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?

I can see that it would be poor style to use it for commonly
encountered errors, but what about truly exceptional errors that would
rarely if ever be encountered?

You want CUnit.

http://cunit.sourceforge.net/

--
Emmanuel
The C-FAQ: http://www.eskimo.com/~scs/C-faq/faq.html
The C-library: http://www.dinkumware.com/refxc.html

..sig under repair
 
C

Christian Bau

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

It depends. You have to assume that your code might do things that you
didn't intend it to do, and you use "assert"s to find these situations.
Such a situation might arise in your production code as well. So you
have to analyse what your code should do in such a situation, and that
depends very much on what the code does and how people are using it.
What about writing a macro that does the same as assert but continues
to work regardless of the state of NDEBUG?

Just put yourself into the role of a user of the software. What will
happen if an assert finds an error? What will be the consequence for the
user? What will happen if an error occurs and the assert doesn't find
it? What will be the consequence for the user then? What consequence
would the user prefer?
I can see that it would be poor style to use it for commonly
encountered errors, but what about truly exceptional errors that would
rarely if ever be encountered?

I would very much hope that your production code doesn't contain any
"commonly encountered errors".
 
M

Michael Wojcik

I would very much hope that your production code doesn't contain any
"commonly encountered errors".

Code does not have to "contain ... commonly encountered errors" in
order to encounter them. It must account for erroneous input,
failures in the implementation (such as memory allocation failure),
and so forth. Those things could be, and are, sometimes trapped with
asserts in development code; Rob, I believe, is correctly noting that
they should not be handled by assert in production code.

My production code is full of error checking, and some of those
branches are not uncommonly taken (for sufficiently generous values
of "common").

Personally, I dislike assert and refrain from using it entirely; in
the time it takes me to insert an assertion, I can write a real, if
minimal, error handler which will do the right thing in both
development and production code.

--
Michael Wojcik (e-mail address removed)

An intense imaginative activity accompanied by a psychological and moral
passivity is bound eventually to result in a curbing of the growth to
maturity and in consequent artistic repetitiveness and stultification.
-- D. S. Savage
 
E

E. Robert Tisdale

Michael said:
Code does not have to "contain ... commonly encountered errors" in
order to encounter them. It must account for erroneous input,
failures in the implementation (such as memory allocation failure),
and so forth. Those things could be, and are, sometimes trapped with
asserts in development code; Rob, I believe, is correctly noting that
they should not be handled by assert in production code.

My production code is full of error checking, and some of those
branches are not uncommonly taken (for sufficiently generous values
of "common").

Personally, I dislike assert and refrain from using it entirely; in
the time it takes me to insert an assertion, I can write a real, if
minimal, error handler which will do the right thing in both
development and production code.

You still appear to be confused about the use of the assert macro.
The assert macro is designed only to trap programming errors (bugs).
You can't "handle" programming errors.
All you can do is fix the bug after assert has detected it
then you don't need any code to trap or "handle" it.
That's why you use the NDEBUG macro definition
to turn off the assert macros after you have finished debugging
and you are ready to release "production" code.

What you call "errors" are more accurately described as *exceptions* --
expected but unpredictable events which cannot be prevented
but must be *handled* when they occur.
 
E

Emmanuel Delahaye

Michael Wojcik wrote on 24/03/05 :
I'm curious why you think a unit-testing tool is relevant to the
question of whether to use asserts in production code.

Do you know this tool? Have you used the CU_ASSERT_xxx() macros
provided by the CUnit framework and library ?

It gives another approach (mainly more detailed and subtle) of the use
of assertions macros.

--
Emmanuel
The C-FAQ: http://www.eskimo.com/~scs/C-faq/faq.html
The C-library: http://www.dinkumware.com/refxc.html

..sig under repair
 
C

CBFalconer

Michael said:
Code does not have to "contain ... commonly encountered errors" in
order to encounter them. It must account for erroneous input,
failures in the implementation (such as memory allocation failure),
and so forth. Those things could be, and are, sometimes trapped with
asserts in development code; Rob, I believe, is correctly noting that
they should not be handled by assert in production code.

My production code is full of error checking, and some of those
branches are not uncommonly taken (for sufficiently generous values
of "common").

Personally, I dislike assert and refrain from using it entirely; in
the time it takes me to insert an assertion, I can write a real, if
minimal, error handler which will do the right thing in both
development and production code.

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".
 
R

Richard Bos

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".

No, if triggered assert() should print a message, usually cryptic and in
any case rarely adaptable, to stderr, where it is hardly ever even seen
by the user, and then crash the program. I'm with Michael here: check
your input before you use it, and if you need an error handler, roll
your own, one which has the chance to do a clean rollback of the
database, write an emergency backup of the user's text, or put a message
on the screen that the user can understand. Leaving this to assert() is
a disservice to the ordinary user.

Richard
 
C

Christian Bau

No, if triggered assert() should print a message, usually cryptic and in
any case rarely adaptable, to stderr, where it is hardly ever even seen
by the user, and then crash the program. I'm with Michael here: check
your input before you use it, and if you need an error handler, roll
your own, one which has the chance to do a clean rollback of the
database, write an emergency backup of the user's text, or put a message
on the screen that the user can understand. Leaving this to assert() is
a disservice to the ordinary user.

Using assert as an error handler is just stupid. assert should be used
to catch bugs in your program - if the condition in the assert evaluates
to false, then you have a bug. Of course, if you think that some error
cannot occur, so you assert instead of handling it, and then the error
_does_ occur, then you have a bug, so the assert was correct in some
perverted way :)
 
R

Richard Bos

Christian Bau said:
Using assert as an error handler is just stupid. assert should be used
to catch bugs in your program - if the condition in the assert evaluates
to false, then you have a bug. Of course, if you think that some error
cannot occur, so you assert instead of handling it, and then the error
_does_ occur, then you have a bug, so the assert was correct in some
perverted way :)

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."

Richard
 
S

Stephen Sprunk

Richard Bos said:
No, if triggered assert() should print a message, usually cryptic and in
any case rarely adaptable, to stderr, where it is hardly ever even seen
by the user, and then crash the program. I'm with Michael here: check
your input before you use it, and if you need an error handler, roll
your own, one which has the chance to do a clean rollback of the
database, write an emergency backup of the user's text, or put a message
on the screen that the user can understand. Leaving this to assert() is
a disservice to the ordinary user.

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

Nobody sane will argue against error-handling code. However, most of the
time error-handling code will silently "correct" the problem and continue on
without notifying anyone, so bugs don't get noticed. In debug builds of the
program, assert()s are useful because they make bugs obvious to
coders/testers, whose data is not important.

Another thing I've picked up over the years is to only use assert() for
_impossible_ conditions, not errors. For instance, if you have a function
that takes a pointer and is _documented_ to never accept NULL, then it's
perfectly valid for a _debug_ build to abort when it gets one. OTOH, a
non-debug build should handle the condition by doing something that makes
sense in context and doesn't crash/abort or otherwise lose user data.

S
 
C

CBFalconer

Christian said:
.... snip ...

Using assert as an error handler is just stupid. assert should be
used to catch bugs in your program - if the condition in the assert
evaluates to false, then you have a bug. Of course, if you think
that some error cannot occur, so you assert instead of handling it,
and then the error _does_ occur, then you have a bug, so the assert
was correct in some perverted way :)

Exactly. There is no such thing as a proven bug free program. The
assert should always be redundant, at least in theory. If it
isn't, found one - lets stomp on it.
 
M

Michael Wojcik

You still appear to be confused about the use of the assert macro.

I have never been confused about the use of the assert macro.
The assert macro is designed only to trap programming errors (bugs).

It's designed to abort a program if its parameter evaluates to zero.
You can't "handle" programming errors.

Perhaps you can't, but I certainly can.
What you call "errors" are more accurately described as *exceptions* --

There is nothing more "accurate" about the label "exception" in this
context.

You're as wrong as usual, Tisdale.
 
M

Michael Wojcik

Michael Wojcik wrote on 24/03/05 :

Do you know this tool?

I haven't used it; I've glanced at the (minimal) documentation
provided on the project's home site and it's Sourceforge site.
Have you used the CU_ASSERT_xxx() macros
provided by the CUnit framework and library ?

No. Did I claim I had?
It gives another approach (mainly more detailed and subtle) of the use
of assertions macros.

Now there, you see, you began to answer my question. Why couldn't
you have provided this information (and, one might hope, something
substantial in addition) in your original post, rather than making
an opaque reference to something outside the standard with no
explanation?

--
Michael Wojcik (e-mail address removed)

Advertising Copy in a Second Language Dept.:
The precious ovum itself is proof of the oath sworn to those who set
eyes upon Mokona: Your wishes will be granted if you are able to invest
it with eternal radiance... -- Noriyuki Zinguzi
 
M

Michael Wojcik

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.

No, I avoid this belief, and routine foo has parameter checks.

This is precisely my point: anywhere I might add an assertion, I can
*just as easily* add a real check and real error handling, and I do
so.
The assert in foo catches your faulty
thinking, and does no harm if you are correct.

The real error handling does no harm if I am incorrect, either,
unlike the assert macro.
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".

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

CBFalconer

Stephen said:
.... snip ...

Another thing I've picked up over the years is to only use assert()
for _impossible_ conditions, not errors. For instance, if you have
a function that takes a pointer and is _documented_ to never accept
NULL, then it's perfectly valid for a _debug_ build to abort when
it gets one. OTOH, a non-debug build should handle the condition
by doing something that makes sense in context and doesn't
crash/abort or otherwise lose user data.

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.

There remains a rarely executed path, that nobody followed, and
that it takes an ingenious fool to trigger. It leads to UB and
heavy loss of data, but no crash. The customer is irate, and you
haven't the vaguest idea what happened. The ingenious fool only
did it by accident, because his girl friend called and mentioned
pregnancy during data entry.

If you had left the assert there would have been a message and a
clue, the heavy data loss wouldn't have occured (after all, you did
allow for power failures and so forth), and the customer, while
still irate, could be reasoned with.

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.

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).
 

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,744
Messages
2,569,483
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top