BRender Technical Reference Manual:3 Functions (Structured Reference):Diagnostic Support
Classifying Failure
Handling Failure
Debug & Release Builds
Reporting Diagnostics
Errors Reported by BRender
Specifying a Diagnostic Handler

Diagnostic Support

Very, very, very few programs are perfect. The compiler may generate incorrect code*1, there may be bugs in the standard C libraries, BRender or the application may be faulty, even the user may upset things through copying the wrong versions of files, say. Certain aspects of the development platform, operating platform, and user configuration may make certain features unavailable. When it comes down to it, you have to expect the unexpected. Fortunately, BRender provides some facilities to assist in dealing with this problem.

The first thing to do is to understand the different situations in which the unexpected can happen (the umbrella term of `Failure' is used). This is covered in the following `Classifying Failure' section. What to do when you spot failure is covered by `Handling Failure' and various relevant techniques are covered in subsequent sections.

Classifying Failure

Build Errors

Build errors are those caught in the build process by the preprocessor, compiler and linker. Syntax, semantic and symbol definition errors are the sort of errors that come under this description.

BRender does not issue any of these errors.

Logic Errors

These are errors in the logic of the program, when it just doesn't quite behave in the desired way. Some errors are caught by the compiler, others are hopefully caught by observation (from programmer to user testing).

Subtle Errors

These are errors caused by limitations of the program environment, e.g. loss of precision, rounding errors, numeric overflow, invalid algorithms, invalid data, etc. The best way of catching these is to use liberal `asserts' throughout the program.

Unexpected Failure

The program can go catastrophically wrong, typically because of memory corruption. Examples of causes that might lead to such failure include: invalid arguments, incorrect bounds, incorrect casting, invalid pointers. The only way of catching these is either to double check everything using `asserts' or to use third party tools that can monitor invalid memory access and dynamic memory use.

Expected Failure

There are known limitations within most computer environments. Time, resources, features, and user correctness are all things that are limited and sometimes the limit is reached. If a program is going to make any effort to avoid crashing it should at least cater for those occasions when failure can be expected.

BRender provides various reporting facilities for handling failure.

Handling Failure

The situations in which failure can be caught are detailed below. Unexpected failure is sometimes caught, but usually for a consequential expected failure. Some systems may be able to detect invalid memory access and in some circumstances, it may be appropriate for the application to handle such exceptions.

Unexpected Program Failure

Errors that should not happen, i.e. assertion failures, should cause an abort in the debug build of an application and a fatal error message, but should be ignored in the release. The point about the use of `assert' is that it states a fact about the state of the program at a particular point. If failure is expected, an assert should not be used, but another handler used instead. Predictably, if an assert would fail in the release, but is properly ignored, it is quite likely that the program will proceed to crash. It is therefore very important to be confident that no assertion can be expected to fail.

Probable, Expected Failure

Those cases where failure is probable and catered for do not warrant any special diagnostic treatment. Examples include: a user specified file not being found, a missing joystick, an unimplemented feature, etc. These are all very much expected failures and are either remedied by the user taking corrective action, or by the program using an alternative. Obviously, the program will need to keep the user informed, but such a failure is not considered an error.

Minor, Expected Failure

Those cases where failure does not significantly affect the program, but is improbable, and possibly surprising, can be considered minor failures. A warning should be issued in the debug build. In the release there will still be some informational evidence of failure, e.g. a greyed-out menu option.

Serious, Expected Failure

An expected failure, but only with a severe remedy available, can be considered a serious failure. An error message can be issued in the debug build, and an information response to the user in the release.

Catastrophic, Expected Failure

Unexpected failure, or expected failure with no remedy available, can be considered catastrophic failure. A fatal error message should be issued in the debug build, followed by abort. In the release build, there should be an informational response. The likely remedy will be exit to the OS/Menu or restarting the application.

Debug & Release Builds

BRender supports the convention of only defining `assert' macros when the DEBUG preprocessor symbol is defined. This together with likely differences in compiler options for debugging support, distinguishes the `debug' build from the `release' build of a BRender application.

For conditional compilation depending upon debug or release builds, always use the defined state of the DEBUG symbol to determine the current build type.

With DEBUG defined BR_ASSERT() and BR_VERIFY() will abort and report failure if their single argument evaluates to zero or Null, without DEBUG defined, BR_ASSERT() is defined as void, and BR_VERIFY() is defined to evaluate its argument. The `verify' version is typically used to assert the value of an expression, that has a required side effect.

The programmer is expected to define ASSERT and VERIFY symbols as equivalent to BR_ASSERT and BR_VERIFY, rather than use ASSERT and VERIFY directly. This can also be achieved by explicitly using #include "brassert.h" in each source file in which this is required.


For debugging an application it is sometimes useful to write progress messages to stderr so that when errors are reported there is some contextual information preceding them. Such messages can also display the values of certain variables. The macro BR_TRACE() is defined for such a purpose, together with BR_TRACE0() to BR_TRACE6() which accept printf() style arguments for formatted output.

Reporting Diagnostics

Warnings, Errors and Fatal Errors are reported using the macros BR_WARNING(), BR_FAILURE(), and BR_FATAL(). Each takes a single string argument, and this is output to stderr. This should be considered a diagnostic level reporting facility and should only be used for testing purposes. The finished application should take a more polished approach and should tailor its response to each type of failure.

There are also formatted versions of these macros taking multiple arguments. The macro has a single digit suffix indicating the number of arguments (from 0 to 6) in addition to the format string - the arguments are the same as for printf().

Use BR_WARNING() to generate a warning or error message, but continue with the program afterwards. Use BR_FAILURE() to generate an error message, and not return, but perform a recovery (exit). Use BR_FATAL() for severe cases where source level information (source file name and line number) should also be reported.

Errors Reported by BRender

BRender may utilise the diagnostics itself in some circumstances, e.g. a missing file, insufficient memory, etc.

Specifying a Diagnostic Handler

A diagnostic handler may be set using BrDiagHandlerSet()116. This may be to report errors more appropriately, e.g. in a dialog box (in a GUI), or to a remote debugging terminal. Or, it could simply be to disable the messages.

The error handler lists pointers to functions handling BR_TRACE(), BR_WARNING(), BR_FAILURE(), and BR_FATAL().

Generated with CERN WebMaker