I\'m looking for ways to obtain the offset of a field in a Delphi record. These 2 following methods work but i was hoping for a cleaner way. Basically i would have liked the
I always use this approach:
Offset := Integer(@rec_a(nil^).c);
Don't let the use of nil^
put you off, it's perfectly safe. And don't worry about 64 bit pointer truncation. If you have a record whose size is >4GB then you have bigger problems!
You could also use a generic approach:
uses
System.SysUtils,TypInfo,RTTI;
function GetFieldOffset( ARecordTypeInfo : PTypeInfo;
const ARecordFieldName : String) : Integer;
var
MyContext: TRttiContext;
MyField: TRttiField;
begin
if (ARecordTypeInfo.Kind <> tkRecord) then
raise Exception.Create('Not a record type');
for MyField in MyContext.GetType(ARecordTypeInfo).GetFields do
if MyField.Name = ARecordFieldName then
begin
Exit(MyField.Offset);
end;
raise Exception.Create('No such field name:'+ARecordFieldName);
end;
And call it like this:
ShowMessage( IntToString( GetFieldOffset( TypeInfo(rec_a),'c')));
Not as fast as your other alternatives, but gives a unified generic solution.
Looking at your options here for a clean solution, it seems the best is to declare a generic function:
function GetFieldOffset( const P : Pointer) : Integer; Inline;
// Example calls :
// GetFieldOffset( @PMyStruct(nil).MyParameter);
// GetFieldOffset( @TMyStruct(nil^).MyParameter);
begin
Result := Integer( P);
end;
So even if the call looks awkward, the function name tells you what's going on. Inlining the call removes the function call overhead, so it will work as a code beautifier.
It is possible to get constant values for a record base and field address:
const
cStruct : MyStruct = ();
cMyInteger3Offs : Pointer = @cStruct.MyInteger3;
cMyStructBase : Pointer = @cStruct;
But this will not make code look cleaner.