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?
As does the definition of the assert macro.
I will never understand this bizarre reliance on assert, when the
language actually contains conditional branching constructs.
It clearly expresses that the condition being tested should never
happen.
Like when you try to open the app's configuration file and it doesn't
exist, or isn't readable, at which point you have many options: log the
reason for the failure to the system log and exit. Pop something up to
alert the user, who can then either fix the problem and continue or
decide to cancel the app run. Create a default config file, alert the
user (or log, or both) and continue or exit. Skip the file entirely, use
defaults from the app, notify the user (and/or log) and continue or exit.
An assert can do some or all of this with enough jiggery-pokery, I
suppose, but it's an ugly way to do it, particularly as all your
wonderful testing instantly vanishes into the ether the second someone
recompiles with NDEBUG.
Designing error traps which vanish that easily is not, IMO, a good idea,
except perhaps in the case where you can guarantee, absolutely, nobody
anywhere under any circumstances whatsoever will ever be able to compile
your code outside your build environment.
Personally, I prefer methods which aren't so fragile.
For a perfect example of this sort of thing, see Malcolm's xmalloc, which
disallows negative size values, *unless* NDEBUG is defined, in which case
it allows them just fine, thanks.