Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Equivalent of __FILE__ and __LINE__

85 views
Skip to first unread message

Martin Harvey

unread,
Aug 20, 2003, 7:37:07 PM8/20/03
to

Hey folks,

Whiilse answering another post, Something occured to me. I have an app
that logs warnings / errors etc to a text file amongst another things.

I'd like something that works like some C compilers that have file and
line macros built into the preprocessor, so I could do this:

Function SimpleLog (Line: string);

SimpleLog( 'An example error' + __FILE__ + __LINE__);

And the string logged would be: 'A simple error Foobar.pas line 684';

Any ideas??

MH.

name

unread,
Aug 20, 2003, 10:38:59 PM8/20/03
to

"Martin Harvey" <mar...@pergolesi.demon.co.uk> wrote in message
news:o108kvsp1v7vkp746...@4ax.com...

i want to see the app! no ideas relevent to your question =\


David Reeve

unread,
Aug 20, 2003, 11:31:46 PM8/20/03
to

"Martin Harvey" <mar...@pergolesi.demon.co.uk> wrote in message
news:o108kvsp1v7vkp746...@4ax.com...
>

I came to the conclusion that without a preprocessor doing this sort of
thing is nigh impossible. If you had a major project with many units it
might well be worth writing your own preprocessor which went through the
list of files in the project and did a textural substitution for tokens as
encountered. If you were really keen you could add it to the toolbar :-).

One thing I miss about C is the lack of preprocessor directives. And one
thing I *dont* miss about C is the way large projects endup in an
indecipherable mess through indiscriminate use of such directives,
particularly in a team programming environment.

Dave

Rob Kennedy

unread,
Aug 20, 2003, 11:44:24 PM8/20/03
to
Martin Harvey wrote:
> I'd like something that works like some C compilers that have file and
> line macros built into the preprocessor, so I could do this:
>
> Function SimpleLog (Line: string);
>
> SimpleLog( 'An example error' + __FILE__ + __LINE__);
>
> And the string logged would be: 'A simple error Foobar.pas line 684';

The JCL has functions that can get you that information from the map
file. http://sourceforge.net/projects/jcl/

You may also want to have a look at madExcept. http://www.madexcept.com/

I've never used either of them, so I'm not endorsing them.

--
Rob

Nicholas Sherlock

unread,
Aug 20, 2003, 11:52:57 PM8/20/03
to

If you are logging exceptions, you should check out MadExcept. JEDI have
also made a unit that would let you find out which line you are running
from. Both of these solutions mean that you have to include a huge .map file
with your .exe (With MadExcept, this is included automatically, compressed
and encrypted).

Cheers,
Nicholas Sherlock


Bjørge Sæther

unread,
Aug 21, 2003, 6:43:44 AM8/21/03
to
"Martin Harvey" <mar...@pergolesi.demon.co.uk> skrev i melding
news:o108kvsp1v7vkp746...@4ax.com...

You could make an IDE plugin, I guess. An "Expert" ?
There are Delphi utilities that take advantage of debug info, to report call
stack with line numbers.
What about Assertions ? Good Idea !

This actually does it:

var
// We need to save the original handler's address
OldAssertHandler: Pointer;

procedure MyAssertErrorHandler(const Message, Filename: string;
LineNumber: Integer; ErrorAddr: Pointer);
begin

// This does the logging
Form1.Memo1.Lines.Add(Message + ' ['+ExtractFileName(FileName)+', line
'+IntToStr(LineNumber)+']');
// And this restores the original Assertion Handler
AssertErrorProc:=OldAssertHandler;
end;

// This does the following:
// 1. sets result = false to bring up an assertion failure
// 2. Redirects assertion handler
function LogIt: boolean;
begin
result:=false;
OldAssertHandler:=AssertErrorProc;
AssertErrorProc:=@MyAssertErrorHandler;
end;


procedure TForm1.Button3Click(Sender: TObject);
begin
Assert(LogIt('Hello !'));
end;

Thanks for trigging me to *finally* find a solution to this !

--
Regards,

Bjørge Sæther
bjorge@haha_itte.no
-------------------------------------
I'll not spend any money on American Software products
until armed forces are out of Iraq.


Michael Brown

unread,
Aug 21, 2003, 6:53:43 AM8/21/03
to
"Bjørge Sæther" <bjorge@hahaha_itte.no> wrote in message
news:xo11b.22675$Hb.3...@news4.e.nsc.no...

Or use the example from the Delphi help (who attribute it to Brian Long) :)

=== BEGIN COPY'N'PASTE ===

unit AssertionHandlerU;
interface

implementation
uses SysUtils, Windows;

procedure AssertErrorHandler(const Message, Filename: string; LineNumber:
Integer; ErrorAddr: Pointer);
var
S: String;
begin
S := Format('%s (%s, line %d, address $%x)',
[Message, Filename, LineNumber, Pred(Integer(ErrorAddr))]);
OutputDebugString(PChar(S));
end;

procedure AssertErrorNoHandler(const Message, Filename: string; LineNumber:
Integer; ErrorAddr: Pointer);
begin
end;

initialization
if FindCmdLineSwitch('Debug', ['/', '-'], True) then
AssertErrorProc := @AssertErrorHandler
else
AssertErrorProc := @AssertErrorNoHandler
end.

=== END COPY'N'PASTE ===

--
Michael Brown
www.emboss.co.nz : OOS/RSI software and more :)
Add michael@ to emboss.co.nz - My inbox is always open


Bjørge Sæther

unread,
Aug 21, 2003, 6:54:34 AM8/21/03
to
"Bjørge Sæther" <bjorge@hahaha_itte.no> skrev i melding
news:xo11b.22675$Hb.3...@news4.e.nsc.no...

> procedure MyAssertErrorHandler(const Message, Filename: string;
> LineNumber: Integer; ErrorAddr: Pointer);
> begin
>
> // This does the logging
> Form1.Memo1.Lines.Add(Message + ' ['+ExtractFileName(FileName)+', line
> '+IntToStr(LineNumber)+']');
> // And this restores the original Assertion Handler
> AssertErrorProc:=OldAssertHandler;
> end;

Oops !
This routine needs of course to be protected, to ensure that the old
provedure pointer is set back also if logging generates an exception:

procedure MyAssertErrorHandler(const Message, Filename: string;
LineNumber: Integer; ErrorAddr: Pointer);
begin

Try
Form1.Memo1.Lines.Add(Message + ' ['+ExtractFileName(FileName)
+', line '+IntToStr(LineNumber)+']');
finally
AssertErrorProc:=OldAssertHandler;
end;
end;

...and of course, MyAssertErrorHandler should rather send log string to a
procedure contained in a procedure pointer. Then you could reuse the code...

Bjørge Sæther

unread,
Aug 21, 2003, 7:08:04 AM8/21/03
to
"Michael Brown" <s...@signature.below> skrev i melding
news:Xy11b.122689$JA5.2...@news.xtra.co.nz...

> "Bjørge Sæther" <bjorge@hahaha_itte.no> wrote in message
> > Thanks for trigging me to *finally* find a solution to this !
>
> Or use the example from the Delphi help (who attribute it to Brian Long)
:)

Talking about reinventing the wheel !
To my defense, I just can't find this in my D5 Ent help file.
Also, my implementation doesn't break regular assertion handling.

--

Martin Harvey

unread,
Aug 21, 2003, 5:40:23 PM8/21/03
to
On Thu, 21 Aug 2003 22:53:43 +1200, "Michael Brown"
<s...@signature.below> wrote:

>"Bjørge Sæther" <bjorge@hahaha_itte.no> wrote in message
>news:xo11b.22675$Hb.3...@news4.e.nsc.no...

>> This actually does it:


>>
>
>Or use the example from the Delphi help (who attribute it to Brian Long) :)
>

Ooooh ... that so precisely does exactly what I want!

WHat's *REALLY* ironic about the whole thing is that I have this in my
code at the moment:

type
TLogSeverity = (SV_INFO, SV_WARN, SV_FAIL, SV_CRIT);
{ These categories basically mean:
For Your Information,
Recoverable problem
Unrecoverable problem (operation aborted),
Internal software error.}

procedure TGlobalLog.Log(Severity: TLogSeverity; Text: string);
var
< vars>
begin
if (Severity = SV_CRIT) and not FLoggingAppException then
Assert(false, Text);

And just for extra amusement value, later on (with a thoroughly choice
comment in the middle ;-) ).

procedure TGlobalLog.LogUnhandledException(Sender: TObject; E:
Exception);
begin
if not (E is EAbort) then
begin
if not FLoggingAppException then
begin
{ No try-finally block here. In the unpleasant case where logging
an
unhandled exception causes an unhandled exception, probably best
to disable logging of unhandled exceptions! }
FLoggingAppException := true;
Log(SV_CRIT, S_UNHANDLED + E.Message);
FLoggingAppException := false;
end;
Application.ShowException(E);
end;
end;

Martin Harvey

unread,
Aug 21, 2003, 5:40:28 PM8/21/03
to
On Thu, 21 Aug 2003 13:08:04 +0200, "Bjørge Sæther"
<bjorge@hahaha_itte.no> wrote:

>Talking about reinventing the wheel !
>To my defense, I just can't find this in my D5 Ent help file.
>Also, my implementation doesn't break regular assertion handling.


Hmm ... AssertErrorProc is not in my Delphi help file, (D4 standard)
although ErrorProc (standard RTL error handler) is.

Amusingly enough, trying to put

procedure TForm1.Button1Click(Sender: TObject);
begin
AssertErrorProc := nil;
end;

into a blank project, and then compiling it works fine!

Hmm ... Must not have been documented back in the dark days of D4.

MH.

VBDis

unread,
Aug 21, 2003, 11:42:31 PM8/21/03
to
Im Artikel <o108kvsp1v7vkp746...@4ax.com>, Martin Harvey
<mar...@pergolesi.demon.co.uk> schreibt:

>I'd like something that works like some C compilers that have file and
>line macros built into the preprocessor

Then just use an C preprocessor to expand the __FILE__ and __LINE__ macros,
before compiling. The Pascal preprocessor does no macro expansion.

Delphi offers to find the location of an error from the address, which is
available with every error message (Find Error...). So there exists no need for
bloating a program with filename strings.

DoDi

Marco van de Voort

unread,
Aug 25, 2003, 10:55:50 AM8/25/03
to
In article <o108kvsp1v7vkp746...@4ax.com>, Martin Harvey wrote:
> line macros built into the preprocessor, so I could do this:
>
> Function SimpleLog (Line: string);
>
> SimpleLog( 'An example error' + __FILE__ + __LINE__);

FPC uses

{$INCLUDE %DATE%}

(and others are FILE,LINE,DATE, and some with FPC- prefixed)

I therefore suspect it has Delphi origins.

Martin Harvey

unread,
Aug 25, 2003, 3:44:53 PM8/25/03
to
On Mon, 25 Aug 2003 14:55:50 +0000 (UTC), Marco van de Voort
<mar...@stack.nl> wrote:

>FPC uses
>
>{$INCLUDE %DATE%}
>
>(and others are FILE,LINE,DATE, and some with FPC- prefixed)
>
>I therefore suspect it has Delphi origins.

I might do a little hunting for that one - thanks a lot.

MH.

Martin Harvey

unread,
Sep 2, 2003, 2:17:30 PM9/2/03
to
Many thanks for all the info. Quick summary.

I've decided to handle it the easy way:

- The log takes all unhandled exceptions and logs them.
- The log can also be called manually to log information.

However, there's no way to actually get the file and line where the
error first occured without calling Assert on that line, so currently,
if assertions are on, the log raises an assertion for critical error
messages simply as a convenent way of getting the debugger to stop the
program. It will of course then log the assertion error again as it
percolates down the call stack (!) but I may remove that situation by
catching the assertion inside the funciton where it is raised.

However, short of putting assertions through the code, and also
additional logging statements as well, there's no simple way of doing
it :-(

MH.

Rob Kennedy

unread,
Sep 2, 2003, 11:33:01 PM9/2/03
to
Martin Harvey wrote:
> Many thanks for all the info. Quick summary.
>
> I've decided to handle it the easy way:
>
> - The log takes all unhandled exceptions and logs them.
> - The log can also be called manually to log information.
>
> However, there's no way to actually get the file and line where the
> error first occured

The ExceptAddr variable should tell you the address where the exception
was raised. From that, you can use some of the JCL routines to determine
the line, procedure, and module names. A map file is required.

--
Rob

Martin Harvey

unread,
Sep 3, 2003, 3:02:29 PM9/3/03
to
On Tue, 02 Sep 2003 22:33:01 -0500, Rob Kennedy <rken...@example.com>
wrote:

>The ExceptAddr variable should tell you the address where the exception
>was raised. From that, you can use some of the JCL routines to determine
>the line, procedure, and module names. A map file is required.

Yes, but the problem is this:!!!


procedure TGlobalLog.Log(Severity: TLogSeverity; Text: string);
var

FinString: string;
NewRec: PStringRec;
LineLen: integer;
PCFinString: PChar;

Rob Kennedy

unread,
Sep 4, 2003, 12:44:43 AM9/4/03
to

My crystal ball tells me that the problem you're talking about is how to
get the line number of the caller. In that case, exceptions and Assert
aren't necessary. Instead, use the JCL again. This time, try the Caller,
function, which gives you the address of the calling function. Or the
LineByLevel function, which will tell you the line number of any caller
in the call stack. There are accompanying functions called ProcByLevel
and FileByLevel, too.

--
Rob

0 new messages