Recent

Author Topic: RTTI static method call  (Read 181 times)

MrShoor

  • New member
  • *
  • Posts: 11
RTTI static method call
« on: May 12, 2019, 01:09:27 am »
I have a record with a static method:
Code: Pascal  [Select]
  1.   TMyRec = packed record
  2.     Bar: Integer;
  3.     class function Layout: IDataLayout; static;
  4.   end;
I need to call this method with RTTI. Does anybody know how to do it?
In Delphi it's possible with such code:
Code: Pascal  [Select]
  1. var ctx: TRttiContext;
  2.     dataType: TRttiType;
  3.     recType: TRttiRecordType;
  4.     method: TRttiMethod;
  5. begin
  6.     ctx := TRttiContext.Create;
  7.     try
  8.         dataType := ctx.GetType(TypeInfo(TRec));
  9.         recType := dataType as TRttiRecordType;
  10.         method := recType.GetMethod('Layout');
  11.         FLayout := System.Rtti.Invoke(method.CodeAddress, nil, method.CallingConvention, method.ReturnType.Handle, method.IsStatic).AsInterface As IDataLayout;
  12.     finally
  13.         ctx.Free;
  14.     end;
  15. end;
I suppose similar code impossible with FPC?

PascalDragon

  • Sr. Member
  • ****
  • Posts: 329
  • Compiler Developer
Re: RTTI static method call
« Reply #1 on: May 13, 2019, 09:29:27 am »
In Delphi it's possible with such code:
Code: Pascal  [Select]
  1. var ctx: TRttiContext;
  2.     dataType: TRttiType;
  3.     recType: TRttiRecordType;
  4.     method: TRttiMethod;
  5. begin
  6.     ctx := TRttiContext.Create;
  7.     try
  8.         dataType := ctx.GetType(TypeInfo(TRec));
  9.         recType := dataType as TRttiRecordType;
  10.         method := recType.GetMethod('Layout');
  11.         FLayout := System.Rtti.Invoke(method.CodeAddress, nil, method.CallingConvention, method.ReturnType.Handle, method.IsStatic).AsInterface As IDataLayout;
  12.     finally
  13.         ctx.Free;
  14.     end;
  15. end;
I suppose similar code impossible with FPC?
In trunk and the upcoming 3.2.0 it is possible to do something similar. You won't get the method listing yet, but the invoke does work:
Code: Pascal  [Select]
  1. program trecrtti;
  2.  
  3. {$mode objfpc}
  4. {$modeswitch advancedrecords}
  5.  
  6. uses
  7. {$if not defined(Win32) and not defined(Win64)}
  8.   FFI.Manager,
  9. {$endif}
  10.   TypInfo, Rtti;
  11.  
  12. type
  13.   IDataLayout = interface
  14.   ['{6541CD8F-19D5-4632-885D-CF1208B0E28E}']
  15.     procedure Foo;
  16.   end;
  17.  
  18.   TTest = class(TInterfacedObject, IDataLayout)
  19.     procedure Foo;
  20.   end;
  21.  
  22.   TMyRec = packed record
  23.     Bar: Integer;
  24.     class function Layout: IDataLayout; static;
  25.   end;
  26.  
  27. { TTest }
  28.  
  29. procedure TTest.Foo;
  30. begin
  31.   Writeln('Foo');
  32. end;
  33.  
  34. { TMyRec }
  35.  
  36. class function TMyRec.Layout: IDataLayout;
  37. begin
  38.   Result := TTest.Create;
  39. end;
  40.  
  41. var
  42.   layout: IDataLayout;
  43. begin
  44.   layout := Invoke(@TMyRec.Layout, Nil, ccReg, PTypeInfo(TypeInfo(IDataLayout)), True, False).AsInterface as IDataLayout;
  45.   layout.Foo;
  46. end.
  47.  
Regarding the unit FFI.Manager: FPC currently does not have a native Invoke implemented for all platforms, so we've decided to use the libffi library. The unit FFI.Manager implements the invoke manager for this. Currently this unit is only compiled for x86_64-linux, so if you want to use it on another platform you either need to adjust packages/libffi/fpmake.pp or copy the unit's source to your project.
On Windows 64 a native Invoke implementation is already provided as is for the register calling convention on i386 targets.

And a small remark regarding your Delphi code: you should be able to simplify it to this (though I did not test it):
Code: Pascal  [Select]
  1.         method := recType.GetMethod('Layout');
  2.         FLayout := method.Invoke(TValue.Empty, Nil).AsInterface As IDataLayout;
  3.  

MrShoor

  • New member
  • *
  • Posts: 11
Re: RTTI static method call
« Reply #2 on: May 16, 2019, 02:59:05 am »
You won't get the method listing yet,
Oh. Getting method address by name passed as a string is actually what I needed. In my project, I want to do some default behavior while method Layout is not defined.
But anyway thank you for your help.