Recent

Author Topic: Function in advanced record: is it wrong to invoke it from pointer to record?  (Read 598 times)

Bart

  • Hero Member
  • *****
  • Posts: 5275
    • Bart en Mariska's Webstek
Hi,

I must be doing something wrong, but I can't see what.

Given the following pieces of code:
Code: Pascal  [Select][+][-]
  1. type
  2.   PHLAttrRec = ^THLAttrRec;
  3.   THLAttrRec = record
  4.     ForeGround: TColor;
  5.     BackGround: TColor;
  6.     Style: TFontStyles;
  7.     function ToString: String;
  8.     procedure Assign(Source: TSynHighlighterAttributes);
  9.     procedure AssignTo(Dest: TSynHighlighterAttributes);
  10.   end;
  11.  
  12.   TPascalAttr = (paAsm,paCaseLabel,paComment,paDirective,paIdentifier,paKeyWord,paNumber,paSpace,paString,paSymbol);
  13.   TPascalAttrRecs = array[TPascalAttr] of THLAttrRec;
  14. ...
  15. function THLAttrRec.ToString: String;
  16. begin
  17.   Result := Format('{ForeGround: %s; BackGround: %s; FontStyle: %s}',
  18.                    [ColorToStr(ForeGround),ColorToStr(BackGround),FontStyleToStr(Style)]);
  19. end;
  20. //will e.g. give: '{ForeGround: clFuchsia; BackGround: clNone; FontStyle: [fsItalic]}'
  21.  
  22. ...
  23. function FindPascalRec(Name: String; Options: TEPlusOptions): PHLAttrRec;
  24. //finds the THLAttrRec that corresponds to Name in an array of THLAttrRec which is a field of the TEPlusOptions record, and returs a pointer to that.
  25.  
  26. //Now:
  27. procedure TOptionsDlgForm.Button1Click(Sender: TObject);
  28. type
  29.   TByteArr = Array[1..SizeOf(THLAttrRec)] of byte;
  30. var
  31.   Attr: TPascalAttr;
  32.   S: String;
  33.   RecPtr: PHLAttrRec;
  34.   i: Integer;
  35. begin
  36.   writeln('TOptionsDlgForm.Button1Click');
  37.   for Attr := Low(TPascalAttr) to Succ(Low(TPascalAttr)) do
  38.   begin
  39.     S := PascalAttrNames[Attr];
  40.     RecPtr := FindPascalRec(S, FOptions);
  41.     writeln('RecPtr=',PtrUint(RecPtr).ToHexString);
  42.     for i := 1 to SizeOf(TByteArr) do write(TByteArr(RecPtr^)[i].ToHexString,#32);writeln;
  43.     //writeln('RecPtr^=',RecPtr^.ToString);
  44.     writeln('RecPtr^.ForeGround=',ColorToStr(RecPtr^.ForeGround),' [',LongInt(RecPtr^.ForeGround).ToHexString,']');
  45.     writeln('RecPtr=',PtrUint(RecPtr).ToHexString);
  46.     for i := 1 to SizeOf(TByteArr) do write(TByteArr(RecPtr^)[i].ToHexString,#32);writeln;
  47.     //writeln('RecPtr^=',RecPtr^.ToString);
  48.     writeln('RecPtr^.ForeGround=',ColorToStr(RecPtr^.ForeGround),' [',LongInt(RecPtr^.ForeGround).ToHexString,']');
  49.     writeln;
  50.   end;
  51. end;

This outputs (as expected given the values those record have):
Code: [Select]
TOptionsDlgForm.Button1Click
RecPtr=0354DB18
00 00 FF 00 FF FF FF 1F 01 00 00 00
RecPtr^.ForeGround=clBlue [00FF0000]
RecPtr=0354DB18
00 00 FF 00 FF FF FF 1F 01 00 00 00
RecPtr^.ForeGround=clBlue [00FF0000]

RecPtr=0354DB24
00 00 FF 00 FF FF FF 1F 01 00 00 00
RecPtr^.ForeGround=clBlue [00FF0000]
RecPtr=0354DB24
00 00 FF 00 FF FF FF 1F 01 00 00 00
RecPtr^.ForeGround=clBlue [00FF0000]

Now uncomment the commented lines in Button1Click and the output changes to:
Code: [Select]
TOptionsDlgForm.Button1Click
RecPtr=0354DB18
00 00 FF 00 FF FF FF 1F 01 00 00 00
RecPtr^={ForeGround: clBlue; BackGround: clNone; FontStyle: [fsBold]}
RecPtr^.ForeGround=RGB($72,$00,$00) [00000080]
RecPtr=0354DB18
80 00 00 00 72 00 00 00 72 AF 84 03
RecPtr^={ForeGround: clMaroon; BackGround: RGB($72,$00,$00); FontStyle: []}
RecPtr^.ForeGround=RGB($72,$00,$00) [00000080]

RecPtr=0354DB24
00 00 FF 00 FF FF FF 1F 01 00 00 00
RecPtr^={ForeGround: clBlue; BackGround: clNone; FontStyle: [fsBold]}
RecPtr^.ForeGround=RGB($B1,$0F,$41) [0384AB01]
RecPtr=0354DB24
01 AB 84 03 B1 0F 41 00 01 AB 84 03
RecPtr^={ForeGround: RGB($01,$AB,$84); BackGround: RGB($40,$50,$19); FontStyle: [fsStrikeOut,fsUnderline]}
RecPtr^.ForeGround=RGB($B1,$0F,$41) [0384AB01]

Notice that the contents of RecPtr^ changes when you call RecPtr^.ToString.
(The adress of RecPtr does not get altered as one can see).

If I do a similar experiment with a THLAttrRec varaible, the contents of that variable will not be changed.

It's appr 1:00 AM here right now and I've been staring at this for over 2 hours and I'm kind of llosing my mind over this.

I'm using fpc 3.2.2 32-bit on Windows.
« Last Edit: February 01, 2023, 10:49:11 am by Bart »

Blaazen

  • Hero Member
  • *****
  • Posts: 3237
  • POKE 54296,15
    • Eye-Candy Controls
Re: Advanced record function: is it wrong to invoke from pointer to record?
« Reply #1 on: February 01, 2023, 03:48:22 am »
Isn't it an uninitialized pointer? I.e. function RecPtr := FindPascalRec(S, FOptions); should return proper nil if it does not find anything and you should check
Code: Pascal  [Select][+][-]
  1. if RecPtr<>nil then ...
before any write out.
Lazarus 2.3.0 (rev main-2_3-2863...) FPC 3.3.1 x86_64-linux-qt Chakra, Qt 4.8.7/5.13.2, Plasma 5.17.3
Lazarus 1.8.2 r57369 FPC 3.0.4 i386-win32-win32/win64 Wine 3.21

Try Eye-Candy Controls: https://sourceforge.net/projects/eccontrols/files/

Bart

  • Hero Member
  • *****
  • Posts: 5275
    • Bart en Mariska's Webstek
Re: Advanced record function: is it wrong to invoke from pointer to record?
« Reply #2 on: February 01, 2023, 10:48:08 am »
If RecPtr were to be nil, the code would raise an exception (all this is compiled with all debugging options checked)?
Anyhow: the output already shows that RecPtr is not nil (becaue in that case PtrUInt(RecPtr) would be zero, and as you can se it is not.

Bart

Bart

  • Hero Member
  • *****
  • Posts: 5275
    • Bart en Mariska's Webstek
As allways it's a PEBKAC problem  :-[

When (in Button1Clik) I hardcoded the PtrRec to point to a specific record in Options, instead of calling FindPascalRec(), all was OK.

So let's look at the definition of this function:
Code: [Select]
function FindPascalRec(Name: String; Options: TEPlusOptions): PHLAttrRec;
Options is passed by value. The consequence is that it retunrs a pointer to the local copy of AOptions somewhere on the (now invalidated) stack of that function.
Passing AOptions by reference solves the issue.

Initially I thought to make it a constref parameter, but the wiki states:
Quote
Note that in general, it should only be used for interfacing with external code or when writing assembler routines.
Hence I decided to make it a var parameter, even though the function is not intended to alter the contents of the Options parameter.

Bart

PS. I can't add [Solved] to the subject line, because of length limitations on the subject line, without altering the subject line in such a way that it would no loger reflect the original question.
« Last Edit: February 01, 2023, 11:18:16 am by Bart »

 

TinyPortal © 2005-2018