Given a record type:
TItem = record
UPC : string[20];
Price : Currency;
Cost : Currency;
...
end;
And the name of a field as a string, how can I get the offset of that field within the record? I need to do this at runtime - the name of the field to access is decided at runtime.
Example:
var
pc : Integer;
fieldName : string;
value : Currency;
begin
pc := Integer(@item); // item is defined and filled elsewhere
fieldName := Text1.Text; // user might type 'Cost' or 'Price' etc
Inc(pc, GetItemFieldOffset(fieldName)); // how do I implement GetItemFieldOffset?
value := PCurrency(pc)^;
..
I'm using Delphi 7.
You can't. Delphi 7 does not emit RTTI for records. There are other options (as seen the previous answers) but those require manual mapping of "Field Name" -> "Offset".
As alex said, Delphi 7 doesn't emit RTTI for records, so you can't retrieve the required info at runtime. However, in later versions (Delphi 2010+) it does, and the following code:
TItem = record
UPC : string[20];
Price : Currency;
Cost : Currency;
//...
end;
var
rttiContext: TRttiContext;
rttiType: TRttiType;
fields: TArray<TRttiField>;
item: TItem;
begin
rttiType := rttiContext.GetType(TypeInfo(TItem));
caption := rttiType.Name + ' {';
fields := rttiType.GetFields;
for i := low(fields) to high(fields) do
begin
caption := caption +'{name='+fields[i].Name+',';
caption := caption +'offset='+IntToStr(fields[i].Offset)+'}';
end;
caption := caption + '}';
will produce 'TItem {{name=UPC,offset=0}{name=Price,offset=24}{name=Cost,offset=32}}'
You can also set the field value in a particular instance (although you should really also verify the type) using:
if fields[i].Name = 'Price' then
fields[i].SetValue(@item, 10);
Following would work for your simplified scenario but I doubt it will be possible to make a generic function for this kind of thing.
The best I can think if is to add some kind of registration object but it still would require you to register all the records you need an offset from.
function GetItemFieldOffset(const Value: string): Integer;
var
item: TItem;
begin
if Value = 'UPC' then Result := 0
else if Value = 'Price' then Result := Integer(@item.Price) - Integer(@item)
else if Value = 'Cost' then Result := Integer(@item.Cost) - Integer(@item)
else raise Exception.CreateFmt('Unhandled condition (%0:s)', [Value]);
end;
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