Reputation: 841
I have run into trouble trying to access the pointer to a record type in my record data using Delphi's RTTI.
Please check the sample code that i have been working on.
// Dummy Header
typDummyHeader = ^tysDummyHeader;
tysDummyHeader = record
MessageCode : Integer;
MessageLength : Integer;
end;
// Dummy record having header and trailer
tysDummyRecord = record
Header : tysDummyHeader;
BotAmount : Double;
SoldAmount : Double;
SoldQty : Int64;
BotQty : Int64;
Tailer : typDummyHeader; // pointer to Dummy Header
end;
TclDummy = class
class function GetFieldValue<T>(const pipInstance : Pointer;
const piclField : TRttiField) : string;
class function ParseAndReturnString<T>(piclObject : T) : string;
end;
var
frmRTTITest: TfrmRTTITest;
implementation
{$R *.dfm}
procedure TfrmRTTITest.FormCreate(Sender: TObject);
var
losDummyRecord : tysDummyRecord;
begin
FillChar(losDummyRecord, SizeOf(tysDummyRecord), #0);
losDummyRecord.Header.MessageCode := 5000;
losDummyRecord.Header.MessageLength := 54433;
losDummyRecord.BotAmount := 19.45;
losDummyRecord.SoldAmount := 34.22;
losDummyRecord.SoldQty := 102;
losDummyRecord.BotQty := 334;
losDummyRecord.Tailer := @losDummyRecord.Header;
ShowMessage(TclDummy.ParseAndReturnString<tysDummyRecord>(losDummyRecord));
end;
class function TclDummy.GetFieldValue<T>(const pipInstance : Pointer;
const piclField : TRttiField) : string;
begin
case piclField.FieldType.TypeKind of
tkFloat: Result := FloatToStr(piclField.GetValue(pipInstance).AsExtended);
tkInt64: Result := IntToStr(piclField.GetValue(pipInstance).AsInt64);
tkInteger: Result := IntToStr(piclField.GetValue(pipInstance).AsInteger);
tkString: Result := Trim(piclField.GetValue(pipInstance).AsString);
end;
end;
class function TclDummy.ParseAndReturnString<T>(piclObject : T) : string;
var
losContext : TRttiContext;
losContextType : TRttiType;
loclField : TRttiField;
losRecordRTTI : TRttiRecordType;
loclRecordField : TRttiField;
losPointerType : TRttiPointerType;
losValue : TValue;
begin
Result := EmptyStr;
losContext := TRttiContext.Create;
losContextType := losContext.GetType(TypeInfo(T));
if losContextType.TypeKind = tkRecord then
begin
for loclField in losContextType.GetFields do
begin
case loclField.FieldType.TypeKind of
tkRecord:
begin
losRecordRTTI := loclField.FieldType.AsRecord;
for loclRecordField in losRecordRTTI.GetFields do
begin
Result := Result + '|' + GetFieldValue<T>(Addr(piclObject), loclRecordField);
end;
end; // tkRecord
tkPointer:
begin
losPointerType := loclField.FieldType as TRttiPointerType;
// Check only record type pointers.
if losPointerType.ReferredType.TypeKind = tkRecord then
begin
losValue := loclField.GetValue(Addr(piclObject));
if (not losValue.IsEmpty) then
begin
for loclRecordField in losPointerType.ReferredType.GetFields do
begin
// Result := Result + '|' + ???????????
end;
end;
end;
end; // tkPointer
else
Result := Result + '|' + GetFieldValue<T>(Addr(piclObject), loclField);
end;
end;
end;
losContext.Free;
end;
In the above sample, when the field is tkPointer
which is pointing to a record type, how do I read the values from that ?
Upvotes: 2
Views: 3643
Reputation: 103
Sorry for my poor English.
Answer of @bummi works but is not right.
It depends on usage.
If you use next code all works well:
var
losDummyRecord : tysDummyRecord;
begin
FillChar(losDummyRecord, SizeOf(tysDummyRecord), #0);
losDummyRecord.Header.MessageCode := 5000;
losDummyRecord.Header.MessageLength := 54433;
losDummyRecord.BotAmount := 19.45;
losDummyRecord.SoldAmount := 34.22;
losDummyRecord.SoldQty := 102;
losDummyRecord.BotQty := 334;
losDummyRecord.Tailer := @losDummyRecord.Header;
ShowMessage(TclDummy.ParseAndReturnString<tysDummyRecord>(losDummyRecord));
But if you use for example this code, parsing don't work correctly:
var
losDummyRecord : tysDummyRecord;
ExternalHeaderVar: tysDummyHeader;
begin
ExternalHeaderVar.MessageCode := 23;
ExternalHeaderVar.MessageLength := 25;
FillChar(losDummyRecord, SizeOf(tysDummyRecord), #0);
losDummyRecord.Header.MessageCode := 5000;
losDummyRecord.Header.MessageLength := 54433;
losDummyRecord.BotAmount := 19.45;
losDummyRecord.SoldAmount := 34.22;
losDummyRecord.SoldQty := 102;
losDummyRecord.BotQty := 334;
losDummyRecord.Tailer := @ExternalHeaderVar;
ShowMessage(TclDummy.ParseAndReturnString<tysDummyRecord>(losDummyRecord));
Solutions is simple and works in both cases well and prevents unexpected errors in the future use:
tkPointer:
begin
losPointerType := loclField.FieldType as TRttiPointerType;
// Check only record type pointers.
if losPointerType.ReferredType.TypeKind = tkRecord then
begin
losValue := loclField.GetValue(Addr(piclObject));
if (not losValue.IsEmpty) then
begin
losValue.ExtractRawDataNoCopy(@NativeIntVar);
for loclRecordField in losPointerType.ReferredType.GetFields do
begin
Result := Result + '|' + GetFieldValue<T>(Pointer(NativeIntVar),loclRecordField);
end;
end;
end;
end; // tkPointer
Upvotes: 1
Reputation: 27385
Result := Result + '|' + GetFieldValue<T>(Addr(piclObject),loclRecordField);
should do the job.
Upvotes: 1