The only part of assert.H
which a normal CoCoALib user might find useful
is the CoCoA_ASSERT
macro: it is intended to be a debugging aid.
The CoCoA_ASSERT
macro does absolutely nothing (not even evaluating its
argument) unless the compilation flag CoCoA_DEBUG
is set. If that flag
is set then the macro evaluates its argument to a boolean result which
is then tested: if the result is true nothing further happens, if the
result is false then the function CoCoA::AssertionFailed
is called with
some arguments indicating which CoCoA_ASSERT
macro call obtained the false
value. The AssertionFailed
function prints out an informative message on
std::cerr
and then throws a CoCoA::ERR::AssertFail
exception.
The file assert.H
contains the following:
- definition of the CoCoA_ASSERT
macro to aid debugging, and the
related function AssertionFailed
During debugging, a debugger can be used to intercept calls to the
function CoCoA::AssertionFailed
which will stop the program just before
throwing the CoCoA::ERR::AssertFail
exception. This should enable one
to find more easily the cause of the problem.
For example, in gdb type
break CoCoA::AssertionFailed
and then go up
(perhaps repeatedly) to the offending line.
The macro name CoCoA_ASSERT
is rather cumbersome, but must contain the
prefix CoCoA_
since macro names cannot be placed in C++ namespaces.
The two definitions of the macro (debugging and non-debugging cases)
both look rather clumsy, but are done that way so that the macro expands
into an expression which is syntactically a simple command. The
definition for the non-debugging case I took from /usr/include/assert.h
;
I do not recall where I got the definition for the debugging case, but
the definition in /usr/include/assert.h
looked to be gcc specific.
The purpose of the procedure AssertionFailed
is explained above in the
user documentation (to facilitate interception of failed assertions). The
procedure never returns; instead it throws a CoCoALib exception with code
ERR::AssertFail
. Before throwing the exception it prints out a message
on std::cerr
summarising what the assertion was, and where it was.
Note the non-standard way of throwing the CoCoA exception: this allows the
ErrorInfo
object to refer to the file and line where CoCoA_ASSERT
was
called (rather then to the line in assert.C
where CoCoA_ERROR
is called).
The entire printed message is assembled into an ostringstream
before being
printed to ensure exception safety: either the whole message is printed or
none of it is, since the printing step is an atomic operation.
Is the exception safe implementation of AssertionFailed
excessive?
You have to use explicitly #ifdef CoCoA_DEBUG
if you want to have a
loop or any other non-trivial piece of code executed only when debugging
it turned on.
The following (simplified but real) code excerpt is mildly problematic:
{ bool OK = ....; CoCoA_ASSERT(OK); }
When compiled without debugging (i.e. CoCoA_DEBUG
is zero) the compiler
(gcc-3) complains that the variable OK
is unusued. It does not appear
to be possible to make the macro "depend on its argument" in the
non-debugging case without incurring the run-time cost of evaluating the
argument (if the argument is just a variable the cost is negligible, but
if it is a more complex expression then the cost could be considerable).
The solution adopted was to modify the calling code like this:
{ bool OK; OK = ....; CoCoA_ASSERT(OK); }
Note that the apparently simpler code below will not work if the elided
code (i.e. the ....
) has a side effect since the elided code will not
be called at all in the non-debugging case:
{ CoCoA_ASSERT(....); }
POSSIBLE SOLUTION: maybe CoCoA_ASSERT
could compute sizeof(...)
in the
non-debugging case -- this should avoid evaluation of the argument, and
will compile away to nothing.