Rationale | |
Statistics for industrial coding efforts indicate that unit tests
often find at least one defect per 10 to 100 lines of code written.
The odds of intercepting defects increase with assertion density.
Use of assertions is often also recommended as part of a strong
defensive coding strategy. Assertions can be used to verify pre-
and post-conditions of functions, parameter values, return values
of functions, and loop-invariants. Because assertions are side-effect
free, they can be selectively disabled after testing in performance-critical code.
A typical use of an assertion would be as follows:
if (!c_assert(p >= 0) == true) {
return ERROR;
}
with the assertion defined as follows:
#define c_assert(e) ((e) ? (true) : \
tst_debugging("%s,%d: assertion '%s' failed\n", \
__FILE__, __LINE__, #e), false)
In this definition, __FILE__ and __LINE__ are predefined by the
macro preprocessor to produce the filename and line-number of
the failing assertion. The syntax #e turns the assertion condition e
into a string that is printed as part of the error message.
In code destined for an embedded processor there is of course no
place to print the error message itself -- in that case, the call
to tst_debugging is turned into a no-op, and the assertion turns
into a pure Boolean test that enables error recovery from anomolous behavior.
|