[All]
An Improved assert() for C++ Builder
Von: David Pankhurst
Inhaltsangabe: The assert() call allows you to test for problems in code, but it is limited. This article details a much-improved assert call.
An Improved assert() for C++ Builder
As part of good programming, assertions in
your code are a convenient way to monitor
what is happening. This article discusses
adding flexibility to assert() to make it
more useful.
Some Background on Asserting
For many years, I've used assert() to manage
code: assert(int test)
This function takes a test and asserts the
test result is true - nothing happens if
so, but false results in an alert, allowing
your program to detect errors conveniently.
The beauty of assert() is that it is also
a macro, meaning that if you #define NDEBUG, the assert macro disappears from your code,
leaving nothing behind.
While this makes it useful for checking for
problems in code, there are some problems
with assert() itself:
- The default for a failed assertion is to
abort the program, making an assertion
a
rather final test.
- assert() tests each time it is reached, making
it awkward to use in a loop.
- It's difficult to examine code; since assert()
normally calls abort(), it's not possible
to fail an assertion and continue debugging.
ASSERTING()
Of course, these problems are exactly those
solved in this article. By modifying assert(),
we get a more robust and useful way to verify
code. The result is a macro package I call
ASSERTING(), both to make it stand out in
code, and to differentiate it from assert().
It is included in the
the example code,
in the files assert2.cpp/.dfm/.h:
ASSERTING(int test,char *errorMessage)
As with the regular assert(), NDEBUG determines whether the code is actually
created or not. It also adds an explanatory
message to help in understanding the error.
The macro in the header file shows how it
should be processed. If NDEBUG is not defined, the following code is expanded
at each ASSERTING() call:
#define ASSERTING(test,msg)
{
if (!(test))
{
static int callIt=1;
if (callIt)
{
if (HandleAsserting(#test,#msg,__FILE__,__LINE__,&callIt))
{ _asm { int 3 } }
}
}
}
The macro uses a static variable to determine
if the call is made. Our subroutine HandleAsserting()
can turn off this static, allowing us to
disable future testing at this location (for
instance, after the first failure in a loop).
Returning true from the function executes
the assembly call 'int 3', which breaks to
the debugger just after the assertion.
A Trio of Options
Combining these features, the HandleAsserting()
call has three options:
- Prevent further testing and 'firing' of the
assertion by setting the static flag to zero.
- Break to the debugger by returning true.
- Continue execution by returning false.
A simple non-VCL implementation does this,
and is included the assert2.cpp file at the
end (but commented out):
int HandleAsserting(char *testStr,char *msgStr,char *fileStr,int line,int *callFlag)
{
// assert message & and return flag regarding aborting:
// callFlag set if repeating forbidden
static char s_text[199]=""; // don't assign dynamically in
// case of 'out of memory' error
wsprintf(s_text,"FAILED: %srn"
"Error: ( %s )rn"
"File '%s', Line %drn"
"Abort execution, allow assert Retry, or Ignore in future?",
msgStr,testStr,fileStr,line);
switch ( ::MessageBox(NULL,s_text,"ASSERTION ERROR",MB_ABORTRETRYIGNORE) )
{
case IDIGNORE: // prevent calling again - turn off flag
*callFlag=0; // never call again
break;
case IDABORT: // return flag and break
return 1; // abort/break
}
return 0;
}
This function calls MessageBox() to display
the assertion failure, and uses the Abort/Retry/Ignore
buttons to get the three possibilities.
While this is perfectly usable, we have Builder
at our beck and call, and of course, a VCL
form can be custom tailored to our needs.
The source code for this article includes
a TRichEdit control, and formats the assertion
error quite vibrantly. The demo program allows
you to fire assertions, and try out the various
options. One caveat about the VCL version
- avoid using it to check items in other
form's constructors.
Conclusion
Assertions are a handy way to ensure code
is acting the way you expect, without adding
code bloat. These options provide additional
features you should find quite useful, expanding
the opportunities for using assertions.
|
|
Connect with Us