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.