unit Asserts;
{
OVERVIEW
Because C/C++ allows #defines, it is much easier to implement a non
intrusive assert() here, we can copy the assert code, but the procedure
call still happens. So, we should at the outer level also ifdef out
the actual call to assert. This can be accomplished by coding the
call to
assert just like normal. Then search for asserts() after you
finish with regular expressions and place the conditionals #ifdefs
at the beginning and end. You must create a (DEFINE of _NDEBUG)
to avoid calling the debug procedures, or create the define in the
projects option of delphi. The address passed back by the assert
module tells you the "find|error" address
to use via the logical segment numbering scheme implemented by Borland's
RunError procedure.
NOTES
We use the conditional define _NDEBUG instead of just the ifopt option for the cases where you don't want debug info, but you want the asserts to stay intact. If you wish, you can put the following in your code to automatically change based on debug info.
I've included the export of the convert addr function from the SysUtils unit, but it is not needed to find the address because return address will return a "Search...FindError..." compatible address. I'm not really sure why borland created the ConvertAddr function, it's still a mystery to me.
(* ifopt D- *)
(* define _NDEBUG*)
(* endif *)
interface
procedure Assert(assertedCond : boolean; const msgStr : string);
function ConvertAddr(Address: Pointer): Pointer; assembler;
implementation
uses WinProcs, SysUtils, WinTypes;
// (Done) Directly stolen(panged) from sysutils unit for use in the
// assert module.
{$STACKFRAMES ON }
function ConvertAddr(Address: Pointer): Pointer; assembler;
asm
TEST EAX,EAX
{ Always convert nil to nil }
JE
@@1
SUB
EAX,OFFSET TextStart
@@1:
end;
// (Done) Error when the assumption made is not correct.
procedure Assert(assertedCond : boolean; const msgStr : string);
{$ifndef _NDEBUG}
var
msgOut : array[0..100] of char;
hexStr : array[0..30] of char;
progOfs : cardinal;
tmpStr : string;
{$endif}
begin
{$ifndef _NDEBUG} // Are we allowed to assert?
if (not assertedCond) then begin
// Get the logical
segment used by the Find|Error menu option of delphi
// Delphi32 is sooo
much nicer, it gives us these handy functions examples in
// ConvertAddr and
ReturnAddr
asm
// Find the return address and back up four past the return address
// so we get the right area for the call from the Assert. Otherwise
// we get one line past the assert.
MOV EAX,[EBP + 4];
SUB EAX, 4
MOV progOfs, EAX
end;
// construct msg\nat
location ssss:oooo using the logical address
StrPCopy(msgOut, msgStr);
tmpStr := chr(10)
+ 'at location ' +
IntToHex(progOfs,SizeOf(progOfs) * 2);
StrPCopy(hexStr, tmpStr);
StrCat(msgOut, hexStr);
MessageBox(0, msgOut,
'Assert Failed', MB_ICONSTOP+MB_SYSTEMMODAL+MB_OK);
// Now, terminate
the program because of assertion problem
halt;
end;
{$endif}
end;
end.