Kelsey said:
[snips]
Let me provide a counter example, I had an embedded product in the
field and we received a number of reports from customers that the
units were
rebooting. When we checked the assertion log of one, we found a
device was generating an interrupt when it should not, which would
have cased bad data to enter the system.
And this is impossible to do with an if (...) which wouldn't abort the
program, but instead give you endless opportunities to do other, more
graceful things - or to simply abort, should that be best?
I will never understand this bizarre reliance on assert, when the
language actually contains conditional branching constructs.
I suppose that use if/else is more work that an assert().
Is it?
When I'm writing code, I tend to write it to deal with the failure
conditions it is going to have to cope with anyways, so I don't see how
it's any more effort to do what I'm already doing in the first place.
What shall I assert on? If x has a value of 2, and I add 1 to it, should
I assert it has a value of 3? No, that's silly. How about, oh, a file
open failure? No, in many cases that is a predictable - and manageable -
condition, assert is the wrong animal.
Hmm. How about, oh, the "update" function in a DB client, where the
update function expects to have an established connection to the server?
No, if I'm doing an update, it means I have "live" data, aborting at this
point would be a most unacceptable thing to do.
Let's see what some other folks have used assert for, right here in this
group.
size_t myStrLen(char const* s)
{
assert( s != NULL );
return strlen (s);
}
Nope, no good. Falls on its face in the presence of NDEBUG.
In another post:
C doesn't allow arrays to be assigned directly. You can use
memcpy:
assert(sizeof my_orders[0].my_mink == sizeof milk);
memcpy(my_orders[0].my_milk, milk, sizeof milk);
Err... it's okay to memcpy if the objects are of incompatible sizes, as
long as NDEBUG is defined? Why?
Another:
I consider this better:
assert(NULL != s);
before calling strlen().
Err... why? Again, the test silently and magically goes away, leaving
the code open to passing a NULL to strlen, if NDEBUG is defined, where a
proper if() test would not silently and magically go away, but continue
handling the case regardless.
This, BTW, led to the comment that assert should never appear in
production code - which, if we apply the thinking, completely invalidates
the use of the assert in the first place. If we risk passing a NULL
where we don't want it, and we're relying on assert to check it, and we
disable assert in the production build, we *also* disable the test for
the NULL - in the production code. This is good practise?
Another:
I put assert() in to check for bugs in my program. For example, if a
certain computation should always produce a number between 0 and N, I
might put in an assert() to verify this. What would be the point of
removing the test in the final version of the program?
No, there wouldn't be any point in removing it... and almost as little
point in simply aborting, when any number of recovery strategies may be
possible, ones which don't simply discard the user's data. Of course,
"what would be the point of removing this in the final version"
implicitly suggests the reliance on the assert "doing its thing" even in
production builds, which is all well and good until your code is compiled
by someone else who thinks asserts should not be in production builds, at
which point all your wonderful error handling just instantly ceased to
exist and your code now pukes and dies on stuff it *should* have handled
gracefully, and would have if you weren't relying on assert as an error
handler.
In case after case of code I see here in CLC, use of assert leads
*directly* to code which fails, miserably, in unpredictable ways, because
it relies on assert to handle errors, rather than using a sensible error
handling strategy. Totally aside from the whole notion that the best way
to handle an error is to abort the app, a notion I find absurd in the
extreme, we're still left with the fact that the whole concept only works
if you can guarantee your code *never* ends up in another build
environment, where the developer using it prefers not to include asserts
in production builds. Poof, there goes all your "error checking".
Yeah, fine, it's more work to write an if statement and handle the
failure condition. Granted. If ease is the excuse, though, we can also
give up all error handling. Just assume files open, allocations work,
network connections succeed, users enter properly formatted data of the
correct length. It's easier, after all, to not bother writing code to
test such things.
Or perhaps the notion is "easy error handling", in which case we can, oh,
simply assert() after a call to fopen, since the user will always type
file names correctly and have appropriate permissions, therefore any
failure to open the file must be a case for the application dying.
It's certainly easier than, say, asking the user for another file name,
or logging that the file couldn't be found or that he lacks permissions
to access it.
In fact, in the (admittedly non-exhaustive) searching I've done, the only
case I've seen so far where assert has any justification in existing is
the following: assert ( INT_MAX >= UCHAR_MAX ).
Mind you, that said, this strikes me as something to test at compile
time, not run time.
Is it easier, is it less work? Sure. Does that necessitate it being a
good thing? Not in the slightest. Most of the uses of assert I see
around here, I wouldn't have on toast.