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
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@
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).
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...)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With