5

This is my first time on this site. Usually, I have no problem to found replies in the old posts but I don't success with my actual problem.

I would like to know how use RTTI functions to know at running time the properties/members of a record under Lazarus/FPC? I know how to do it for a class (Tpersistent descendant and published properties) but not for FPC. Some links indicates how to do it under Delphi (From D2010), but I don't know how to transpose it under Lazarus.

Thanks in advance for help and assistance. Salim Larhrib.

To kevin : As I told before, this is my first demand. But I understand. You are right. This is my code

procedure TMainForm.btRecordTHashListClick(Sender: TObject);
var
  pTData    : PTypeData;
  pTInfo    : PTypeInfo;
  TablePtr  : PatableRecord;
  Loop      : Integer;
begin
  // Set of Record pointers + HashList

  // Create Container
  if  not Assigned(FTableRecList) then FTableRecList := TFPHashList.Create;

  // Insert data
  new(TablePtr);
  TablePtr^.description := 'Dictionnaire des tables.';
  FTableRecList.add('atable', TablePtr );

  new(TablePtr);
  TablePtr^.description := 'Dictionnaire des fonctions.';
  FTableRecList.add('afunction', TablePtr );

  new(TablePtr);
  TablePtr^.description := 'Dictionnaire des listes d''option.';
  FTableRecList.add('alist', TablePtr );

  // Read records
  for Loop:=0 to FTableRecList.Count-1 do
  begin
    TablePtr := FTableRecList[Loop];
    ShowMessage('Parcours Index : ' + TablePtr^.description);
  end;

  // Find records
  try
    TablePtr := FTableRecList.Find('ddafunction');
    ShowMessage('Record finded : ' + TablePtr^.description);
  except
    ShowMessage('Not such record .');
  end;
  try
    TablePtr := FTableRecList.Find('afunction');
    ShowMessage('Record finded : ' + TablePtr^.description);
  except
    ShowMessage('No such record.');
  end;

  // Free memory : To put later in TFPHashList wrapper
  for Loop:=0 to FTableRecList.Count-1 do Dispose(PatableRecord(FTableRecList[Loop]));

// RTTI
  pTInfo := TypeInfo(TatableRecord);

  pTData := GetTypeData(pTInfo);
  ShowMessage('Member count = '+IntToStr(pTData^.PropCount));
end;
  • 1
    Free Pascal should have quite good Delphi compatibility once you turn on the corresponding [compiler mode](http://www.freepascal.org/docs-html/user/userse33.html). So following answer may be useful http://stackoverflow.com/a/23824290/2626313 (I used Google: `site:stackoverflow.com delphi rtti record` to find it) – xmojmr Jan 06 '15 at 19:40

1 Answers1

6

WARNING: It works with FPC 2.7.1 or later.

You can deal with record fields using pointers. Here is example:

program rttitest;

uses
    TypInfo;

type
    TMyRec = record
        p1: Integer;
        p2: string;
    end;

var
    td: PTypeData;
    ti: PTypeInfo;
    mf: PManagedField;
    p: Pointer;
    f: Pointer;

    r: TMyRec;

begin
    r.p1 := 312;
    r.p2 := 'foo-bar';

    ti := TypeInfo(r);
    td := GetTypeData(ti);

    Writeln(td^.ManagedFldCount); // Get count of record fields

    // After ManagedFldCount TTypeData contains list of the TManagedField records
    // So ...
    p := @(td^.ManagedFldCount); // Point to the ManagedFldCount ...
    // Inc(p, SizeOf(Integer)); // Skip it (Wrong for 64-bit targets)
    // Next line works for both
    Inc(p, SizeOf(td^.ManagedFldCount)); // Skip it

    mf := p; // And now in the mf we have data about first record's field
    Writeln(mf^.TypeRef^.Name);

    Write(r.p1); // Current value
    f := @r;
    Inc(f, mf^.FldOffset); // Point to the first field
    Integer(f^) := 645; // Set field value
    Writeln(r.p1); // New value

    // Repeat for the second field
    Inc(p, SizeOf(TManagedField));
    mf := p;
    Writeln(mf^.TypeRef^.Name);

    Write(r.p2);
    f := @r;
    Inc(f, mf^.FldOffset);
    string(f^) := 'abrakadabra';
    Writeln(r.p2);


    Readln;
end.
Abelisto
  • 14,826
  • 2
  • 33
  • 41
  • 1
    https://ideone.com/ supports language `Pascal (fpc) (fpc 2.6.2)` but your snippet does not compile there as-it-is due to `Error: Identifier not found "PManagedField"`. Can you create an executable proof that your code is working? – xmojmr Jan 07 '15 at 05:53
  • 2
    @xmojmr Yes. Sorry. It just because i am using latest svn trunk of the FPC (3.1.1) for now. And latest stable FPC (2.6.4) still not include this feature. – Abelisto Jan 07 '15 at 08:07
  • I use fpc 2.6.4. I will update it and revert. Anyway, thank you for help. – Salim Larhrib Jan 07 '15 at 08:31
  • It works very well with fpc 3.1.1. Is there a way to get the property name?? Because mf^.TypeRef^.Name gives type name. – Salim Larhrib Jan 07 '15 at 18:52
  • @SalimLarhrib As I can see in the source code of the typinfo - no, just because names of the record fields not stored anywhere in the RTTI data. So if you need to work with fields by name you have to use classes instead of records. – Abelisto Jan 07 '15 at 19:20
  • I'm using Free Pascal Compiler version 3.1.1 [2015/11/25] for x86_64 and the program crashes in Writeln(mf^.TypeRef^.Name); ¿What could be? – Luciano Lorenti Nov 25 '15 at 15:01
  • 1
    @LucianoLorenti I fix the code. Error was at line `Inc(p, SizeOf(Integer)); // Skip it`, just change it to `Inc(p, SizeOf(td^.ManagedFldCount));` Good luck. – Abelisto Nov 25 '15 at 17:57