Graded Asserts and ErrorLogger-Awareness
----------------------------------------
Graded Asserts
--------------
The problem this addresses is that the granularity of the C assert mechanism is
inadequate. The assert() macro is avtive whenever NDEBUG is not defined.
For some assertions this is apprpriate. However, for others the coder may well
know that this assertion is unlikely to become time critical, and ought to
remain active even if some degree of optimization has been specified.
The mechanism we use to address this must obey the following rules:
1 - The standard assert mechanism, activated by lack of NDEBUG, must continue
to behave as it now does (except that it is permissible to have additional
defines available that would deactivate asserts). Preferably, the
existing system-supplied assert macro will continue to be used.
2 - The behavior of any new assert-like macros, when triggered, must be the
same as when an assert is triggered.
3 - The activation of the new assert-like macros is again controlled by
defines. The gradations of asserts will be reflected in the gradations
of available defines. The gradations must form a simple sequence: If
one level is deactivated, all "more transient" levels of assertions must
also be deactivated.
4 - The assert-like macros must, in the case where they are not activated,
preprocess down to no code at all ,so that there is no question of
removing them "just in case the optimizer isn't that good."
Note that unless coders start employing the new graded forms of assert,
the granularity will of course remain either all asserts active, or none.
Should the new graded asserts be less readily deactivated than assert, or
more readily deactivated. In other words, if NDEBUG (and nothing else)
is defined, then ordinary assertions are inactive; should the graded
assertions be active or not? If graded asserts are deactivated, should this
also deactivate ordinary assrts?
There are two attiudes one can take toward this issue. On the one hand is
the view expressed by M_, who says "I freely seed my code with asserts, even
in performance-critical loops, because I know that when any attention is paid
to speed, they will all go away. I don't want to lose this -- if I have an
assert that I want to stick around despite some level of optimization, I will
say so explicitly!" This argues that the symbols that deactivate the new
graded asserts should also cause NDEBUG to be defined.
On the other hand, the advice found in many software development books and
articles is that you don't want to turn off assertions when going into
production, because any problem that do emerge will catch you with less
debugging capability than you had while developing the product, so that the
extra information from a timely abort can be more crucial. If you believe
this canard, your attitude should be "Ordinarily, all assertions should
stick around. You should have to do something special to make a particular
assertion disappear easily (because it is time-critical)." This argues that
the most commonly used deactivating define should leave standard asserts
active (should leave NDEBUG undefined).
Both viewpoints have merit, and out solution can accomodate both philosophies.
What we provide is as follows:
* A sequence of macros assert1, assert2, assert3 to represent
more transient assertions than the ordinary assert. That is, if
a coder wishes that a particular assertion be deactivated on any
excuse whatsoever, she could use assert3. These assertN macros, if
active use precisely the ordinary system assert mechanism.
* A set of defines NDEBUG1 NDEBUG2 NDEBUG3 will deactivate these
more transient asserts. That is, NDEBUG deactivates them all,
NDEBUG2 deactivates assert2 thru assert3, and so forth. NDEBUG3 is
the weakest of all, turning off only those asserts designated as the
most readily deactivated.
* A macro sanityCheck to represent an assert that will NOT are harder
deactivated in an NDEBUG compile. This should be used where the coder feels
there is little performance cost of applying the check, or there is a real
worry that someday a rare data-dependant condition might trip the sanity
check. The syntax is the same as for assert. The define NOSANCHECK will
disable these sanity checks.
The user can access these by including assertN.h.
ErrorLogger-Aware Graded Asserts
--------------------------------
It is desirable that the reporting of errors -- even assertion-failures -- be
channeled through the ErrorLogger mechanism if that is in use. We say this
based on the desire to format the error message in the standard way and route
it to the designated destinations.
It is also plausible that a framework wants to continue in some manner despite
a failed (user) assertion. Without pre-judging whether it can ever be
wise to continue processing beyond a failed assertion, we can provide that
option.
The mechanism we use to address this should obey the following rules:
1 - The ELasserts should have the same semantics as the graded asserts
discussed above.
2 - When an ELassert is triggered, it should route to errlog an error
message containing the text of the failed assertion, and the file and
line of the failed assertion. (This implies that the user or framework
MUST have set up the ErrorLog errlog.) This error message should have
severity ELabort. The framework is, of course free to establish that
ABORT messages don't actually abort the job.
3 - The activation of the ELassert macros is controlled by the same defines
as would control the gradations of ordinary assertions and sanity checks.
4 - The ELassert macros must, in the case where they are not activated,
preprocess down to no code at all ,so that there is no question of
removing them "just in case the optimizer isn't that good."
Sometimes one wishes to discover whether some (assumedly rare) circumstance
ever actually happens, but not to cause the program to die if it does. And
of course it would be nice if that "curiousity check" could be disabled for
production running. This can be nicely integrated with the ELassert package:
5 - A separate set of ELassert macros should provide precisely the same
behavior except that the error messages they issue if triggered would have
severity ELwarning.
What we provide is as follows:
ELassert behaves like assert (deactivated by NDEBUG)
but issues an ELabort message if triggered.
ELassert1, ELassert2, ELassert3 behave like assert1, assert2, assert3
but issue an ELabort message if triggered.
ELsanityCheck behaves like sanityCheck (deactivated by
NOSANCHECK) but issues an ELabort message
if triggered.
ELwarningAssert behaves like assert (deactivated by NDEBUG)
but issues an ELwarning2 message if
triggered.
ELwarningAssert1, behave like assert1, assert2, assert3
ELwarningAssert1, but issue an message with severity
ELwarningAssert3 ELwarning if triggered.
ELwarningCheck behaves like sanityCheck (deactivated by
NOSANCHECK) but issues an ELwarning
message if triggered.