Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I get the address of a field if it's a procedure or function pointer?

I have to do some record sub-data size calculation so created something like

function GetSubDataSize(const Rec: TRecord): integer;
begin
  Result:=integer(@Rec.Field2) - integer(@Rec.Field1);
end;

Everything is ok except for one case, if one of Field is a procedure or function pointer, so in case of

TRecord = record
   Field2: procedure(Sender: TObject) of object;
end;

The function gets the address of the procedure itself. Is there a way to typecast the field to get the address of the field not the address of the function? I know that I can solve it with variant part records, but just prefer not to use it.

Thanks,

Max

like image 880
Maksee Avatar asked Jul 07 '10 10:07

Maksee


3 Answers

Have you tried @@?

type
  TRecord = record
    Field1:integer;
    Field2:TNotifyEvent;
  end;

function GetSubDataSize(const Rec: TRecord): integer;
begin
  result := integer(@@Rec.Field2) - integer(@Rec.Field1);
end;

Gives me 8, which is what I would expect on Delphi 2010.

N@

like image 177
Nat Avatar answered Nov 12 '22 16:11

Nat


As your Field2 is a method pointer, you should make use of that:

This code

type
  RRecord = record
    Field1: Integer;
    Field2: procedure (Sender: TObject) of object;
    Field3: Integer;
  end;

var
  rec: RRecord;
begin
  Memo1.Lines.Add(Format('@rec.Field1 %d', [Integer(@rec.Field1)]));
  Memo1.Lines.Add(Format('@rec.Field2 %d', [Integer(@rec.Field2)]));
  Memo1.Lines.Add(Format('@TMethod(rec.Field2).Code %d', [Integer(@TMethod(rec.Field2).Code)]));
  Memo1.Lines.Add(Format('@TMethod(rec.Field2).Data %d', [Integer(@TMethod(rec.Field2).Data)]));
  Memo1.Lines.Add(Format('@rec.Field3 %d', [Integer(@rec.Field3)]));
end;

put in the OnCreate of a form with a memo on it, produces:

@rec.Field1 1244820
@rec.Field2 4052
@TMethod(rec.Field2).Code 1244828
@TMethod(rec.Field2).Data 1244832
@rec.Field3 1244836

The second line shows a random value, as nothing was assigned to the local record variable. The third and fourth lines show the addresses of the TMethod members.

Please note that there is (may be) filling in the record because of the fact that methods seems to be always 8 byte aligned. (At least in D2009/D2010).

like image 32
Marjan Venema Avatar answered Nov 12 '22 16:11

Marjan Venema


Unfortunately, there doesn't seem to be any easy way to do this the way you're doing it. Variant part records would probably work, but I agree, that's kinda ugly.

But if you're on Delphi 2010, there's a better way. It looks like you're trying to go over all the fields of the record and determine their offsets. That can be done with extended RTTI, like so:

procedure test;
var
   ctx: TRttiContext;
   recType: TRttiType;
   recField: TRttiField;
begin
   ctx := TRttiContext.Create;
   recType := ctx.GetType(TypeInfo(TMyRec));
   for recField in recType.GetFields do
     writeln(format('Field %s is at offset %d.', [recField.Name, recField.Offset]));
end;

(The writeln assumes you're in a console app written purely for demonstrative purposes, like I wrote up to test this. Feel free to modify it to suit your needs...)

like image 1
Mason Wheeler Avatar answered Nov 12 '22 14:11

Mason Wheeler