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.