ASSERT_CPP(3C++)

NAME
Assert_cpp (1.3) - Improved assert macro for C++: simple debugging aid.

SYNOPSIS

This file replaces assert.h and provides several extra features. First, it can print the call stack if an assertion fails or a signal happens. Second, if you're really in trouble, it can print entry into and exit from functions. Third, a new variant of assert is declared that can print a user-defined value before aborting, to give the programmer a better idea why it aborted. This package could improve feedback between users and developers in complex programs that have been released, but have residual bugs.

USAGE
How to include it:
#include assert_cpp.h

Contents:

#ifndef ASSERT_CPP_H
# define ASSERT_CPP_H
# ifndef C
#  define C const
# endif
# ifndef STD_H
 typedef short BOOL;	/* Use BOOL for logical variables as a reminder to
						the programmer. */
# endif

class assert_assert	{
	public:		// some functions and data not shown
	/* May be called to trace pseudo-stack: */
	static void trace();

	/* Parameters to be set by user: */
	static BOOL desperate;
	static void (*display_msg)(C char *s);
	static void (*terminate)(int);
	};

extern assert_assert assert_tmp_rnmn;
#endif

#define assert(x) /* not shown */
#define assert2(x,y) /* not shown */
#define ENTER_FCN(x) /* not shown */
#define MARK_LINE /* not shown */

DESCRIPTION

The class assert_assert is used to maintain a stack of calls to ENTER_FCN. It is not intended to be used directly.

The user can call ENTER_FCN("any text here") once in any block (typically, this would be called once per function). ENTER_FCN remembers the filename, the supplied text, and the line number. Calls of MARK_LINE can be scattered through the program at will; they will update the line number information that was stored by ENTER_FCN.

When an assertion fails, or a signal is caught, the stored information is dumped into (*assert_assert::display_msg)(const char *), which, by default, flushes cout and places its argument on the cerr stream. Any user supplied function may be provided if the default is not to the user's liking. Next, (*assert_assert::terminate)() is called to stop the process, with an argument of 1 (for failed assertions) or 128 + the signal number for caught signals.

The default terminate calls exit. Display_msg and terminate are given a default value in def_msg.c so that the user can supply his or her own defaults. The package is not tied to any i/o model. If terminate == 0, the default action will be abort(), while a zero value of display_msg will cause an abort when anything would otherwise be displayed.

Assert functions just like the normal assert(3); it prints a message and aborts the process. Assert2(x, y) is similar, but y is an expression which is evaluated just before the stack is printed and the program is aborted. A typical use would be to print something (e.g. assert2(x==0, cerr<<x<<endl);). The second argument should not contain a semicolon.

If the variable assert_assert::desperate is set nonzero, every pass through ENTER_FCN or MARK_LINE will produce a line of printout, as will every exit from a marked function. The consistency of the stack will also be checked, and the program terminated if there are problems. Finally, if the macro NDEBUG is defined, the package will not be included. This package may be turned on and off by re-including assert_cpp.h with NDEBUG either defined or undefined.

This package will catch all catchable signals that are set to SIG_DFL. It will, therefore, ignore signals that might have been caught by a user routine. It is also safe even if the user catches some signals in a global or static constructor; the package will not catch signals that have been changed from their default value. The only possiblity of problems is if the user depends on signals having their default setting.

Example

#include "assert_cpp.h"
#include <iostream.h>
void foo(void)
{
 ENTER_FCN("foo()");					// line 14
 assert2(0, cerr << "assert_2 in foo");	// line 15
}
main()
{
 ENTER_FCN("main()");
 int a, b;
 MARK_LINE;
 assert(&a != &b);
 MARK_LINE;								// line 24
 {
 ENTER_FCN("main-block");				// line 26
 }
 foo();
}

Output:

assert_2 in foo						// printed on line 15
Assertion failed:  0  Line: 15 File: assert_test.c
During constructors:GLOBAL SCOPE:0
assert_test.c:main():24
assert_test.c:foo():14
ABORT

One could even define (*display_msg)() to write to a file, and (*terminate)() to mail the results back to the developers.

BUGS
MARK_LINE only records the line number; if you have a function with a MARK_LINE, but no ENTER_FCN, the filename and function name can be wrong. Similarly, if you have uninstrumented functions, the printed stack will be incomplete, but what is there will be correct.
Sufficiently bad problems (like bus errors or segmentation violations) can corrupt the output. The package makes a reasonable attempt to check the stack for consistency, and tries to print out as much as is meaningful. The stack checking only looks at the assert_assert classes, so it is certainly not going to catch all problems. It will detect, with a high probability, if the data private to this class has been overwritten.

See also
assert(3), signal(2)