Lazarus

Programming => General => Topic started by: mtournay on February 19, 2020, 11:50:31 am

Title: [Solved] RTTI headache
Post by: mtournay on February 19, 2020, 11:50:31 am
Hi all

since this morning i'm trying to get access to properties and methods of a class using rtti

I believe it's wonderfull and it's working (for others) but I can't make it working for me !

I crawled accross many pages (this forum, SO, laz wiki...)

this simple code said there's no property  :o

I've tried replacing sampleclass by tstringlist, same result

Code: [Select]
program rtti;

uses typinfo;

type

  { TSampleClass }

  TSampleClass = class
  private
    Fi: integer;
    procedure SetI(AValue: integer);
  public
    procedure a;
    function b: boolean;

    property i: integer read Fi write SetI;
  end;

  { TSampleClass }

procedure TSampleClass.SetI(AValue: integer);
begin
  if Fi = AValue then Exit;
  Fi := AValue;
end;

procedure TSampleClass.a;
begin
  writeln('a');
end;

function TSampleClass.b: boolean;
begin
  writeln('b');
  result := true;
end;

var
  SampleClasse: TSampleClass;
  info:PTypeInfo;
  Data: PTypeData;

begin
  SampleClasse := TSampleClass.Create;

  info := TypeInfo(SampleClasse);
  Data := GetTypeData(info);

  Writeln('Type name : ', info^.Name);
  Writeln('Type kind : ', GetEnumName(TypeInfo(TTypeKind), Integer(info^.Kind)));
  Writeln('Properties count : ', data^.PropCount);

  readln;
end.

At last, when someone nice explain to me who it's work, I will write a wiki page !
Title: Re: RTTI headache
Post by: korba812 on February 19, 2020, 12:02:07 pm
Currently only published properties are available. You must put your properties in the published section.

Code: Pascal  [Select][+][-]
  1.  TSampleClass = class
  2.   private
  3.     Fi: integer;
  4.     procedure SetI(AValue: integer);
  5.   public
  6.     procedure a;
  7.     function b: boolean;
  8.   published // <----
  9.     property i: integer read Fi write SetI;
  10.   end;
  11.  
Title: Re: RTTI headache
Post by: Thaddy on February 19, 2020, 12:30:57 pm
did you try trunk? and declare the classes with {$M+}?
Title: Re: RTTI headache
Post by: howardpc on February 19, 2020, 12:47:25 pm
You don't need trunk, nor a redundant {$M+}.
 As korba812 writes, rtti has been available for published properties for a long time in FPC.
Versions since 3.0.4 have merely extended the available functionality, adding to an already extensive base.
Title: Re: RTTI headache
Post by: Thaddy on February 19, 2020, 12:56:17 pm
extended rtti extends beyond published
Title: Re: RTTI headache
Post by: mtournay on February 19, 2020, 02:25:14 pm
Thanks all

first, i will add my configuration to my signature : laz 2.06 32b + fpc 3.04 32b / Win10 64b

ok for puhlished properties. I don't want to use trunk (company policy), i changed my code that way

Code: [Select]
program rtti;

uses typinfo;

type

  { TSampleClass }

  {$M+}
  TSampleClass = class
  private
    function doSomething: string;
  published
    property Something: string read doSomething;
    function somethingElse(Param1, Param2: integer): string;
  end;
  {$M-}

  { TSampleClass }

  TCallFunc = function: string of object;

function TSampleClass.doSomething: string;
begin
  result := 'something';
end;

function TSampleClass.somethingElse(Param1, Param2: integer): string;
begin
  result := 'somethingElse';
end;

var
  SampleClasse: TSampleClass;
  info:PTypeInfo;
  Data: PTypeData;
  propInfo: PPropInfo;
  CallFunc : TCallFunc;
  r: string;

begin
  SampleClasse := TSampleClass.Create;

  info := TypeInfo(SampleClasse);
  Data := GetTypeData(info);

  Writeln('Type name : ', info^.Name);
  Writeln('Type kind : ', GetEnumName(TypeInfo(TTypeKind), Integer(info^.Kind)));
  Writeln('Properties count : ', data^.PropCount);

  propInfo := GetPropInfo(SampleClasse, 'SomeThing');
  if assigned(propInfo) then begin
    callfunc := TCallFunc(propInfo^.GetProc^);
    r := CallFunc();
  end;

  readln;
end.

Point 1 : how to call something() to get result in r (current code don't work, it end up at callfunc() )
Point 2 : with {$M+}, fpc 3.04 allows me to publish a function, but is there a way to found it, access it, and then call it ?

(ho btw I've made lot of php last monthes  :D )
Title: Re: RTTI headache
Post by: rvk on February 19, 2020, 03:01:50 pm
Point 1 : how to call something() to get result in r (current code don't work, it end up at callfunc() )
Your "Something" was a string property. Not a function.

So:

Code: Pascal  [Select][+][-]
  1.   propInfo := GetPropInfo(SampleClasse, 'SomeThing');
  2.   if assigned(propInfo) then
  3.   begin
  4.     // callfunc := TCallFunc(propInfo^.GetProc^);
  5.     // r := CallFunc();
  6.     r := GetStrProp(SampleClasse, PropInfo);
  7.     writeln(r);
  8.   end;
Title: Re: RTTI headache
Post by: mtournay on February 19, 2020, 03:06:42 pm
Quote
Your "Something" was a string property. Not a function.

argh you are so right ! what a dumb I made i can use it directly

It has a getProc, so my first attempt was to call it :)

thank you !
Title: Re: RTTI headache
Post by: PascalDragon on February 20, 2020, 10:02:18 am
You don't need trunk, nor a redundant {$M+}.

The {$M+} is required, because a class that does not inherit from a class that has {$M+} enabled can not use published properties. Yes, in Delphi this was changed that published implicitly enables type information, but FPC does not support that yet.

Quote
Your "Something" was a string property. Not a function.

argh you are so right ! what a dumb I made i can use it directly

It has a getProc, so my first attempt was to call it :)

thank you !

As a property can have both direct field access and methods as getters and setters you are better advised to use the Get*Prop and Set*Prop routines (as rvk showed) as those already deal with that.
Title: Re: RTTI headache
Post by: rvk on February 20, 2020, 11:42:40 am
Getting to a public function is a lot harder.

I see FPC hasn't implemented an ObjectInvoke (ObjAuto module) (http://docwiki.embarcadero.com/CodeExamples/Rio/en/ObjectInvoke_(Delphi)) yet.
At least not like Delphi.

Only for interfaces:
https://forum.lazarus.freepascal.org/index.php/topic,45379.msg321025.html#msg321025

Title: Re: RTTI headache
Post by: Thaddy on February 20, 2020, 11:52:22 am
I see FPC hasn't implemented an ObjectInvoke (ObjAuto module) (http://docwiki.embarcadero.com/CodeExamples/Rio/en/ObjectInvoke_(Delphi)) yet.
At least not like Delphi.
Yes that is true. Sven indicated it can take quite some time. At the moment the calls are stubs waiting to be implemented.
Title: Re: [Solved] RTTI headache
Post by: PascalDragon on February 24, 2020, 02:41:46 pm
Calling already works as Rtti.Invoke is implemented in 3.2 and newer (though it relies on the FFI library for most systems currently). It's retrieving non-published functions which is not implemented currently.

Code: Pascal  [Select][+][-]
  1. program tcalltest;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. uses
  6.   Rtti, TypInfo{, FFI.Manager};
  7.  
  8. procedure MyProc(aArg1: String; aArg2: LongInt);
  9. begin
  10.   Writeln(aArg1, ': ', aArg2);
  11. end;
  12.  
  13. type
  14.   TMyObject = class
  15.     f: LongInt;
  16.     procedure DoTest;
  17.   end;
  18.  
  19. procedure TMyObject.DoTest;
  20. begin
  21.   MyProc('My Object', f);
  22. end;
  23.  
  24. var
  25.   v1, v2: TValue;
  26.   t: TMyObject;
  27. begin
  28.   v1 := TValue.specialize From<String>('The value');
  29.   v2 := TValue.specialize From<LongInt>(42);
  30.   Invoke(@MyProc, [v1, v2], ccReg, Nil, True, False);
  31.   t := TMyObject.Create;
  32.   t.f := 21;
  33.   v1 := TValue.specialize From<TMyObject>(t);
  34.   Invoke(TMethod(@t.DoTest).Code, [t], ccReg, Nil, False, False);
  35. end.

Code: [Select]
The value: 42
My Object: 21

The FFI.Manager unit  (together with the FFI library) is currently required for all targets except for x86_64-win64 and for routines with register calling convention on any i386 target.

I see FPC hasn't implemented an ObjectInvoke (ObjAuto module) (http://docwiki.embarcadero.com/CodeExamples/Rio/en/ObjectInvoke_(Delphi)) yet.
At least not like Delphi.

Please note that ObjectInvoke is an old form of invoking code at runtime and was superseded by the functionality of the Rtti unit in Delphi 2009 and newer as it is much more powerful. We are targeting that and after we have that we might implement a ObjAuto compatible interface that uses the Rtti unit.
Title: Re: [Solved] RTTI headache
Post by: rvk on February 24, 2020, 02:47:24 pm
Calling already works as Rtti.Invoke is implemented in 3.2 and newer (though it relies on the FFI library for most systems currently). It's retrieving non-published functions which is not implemented currently.
Code: Pascal  [Select][+][-]
  1.   Invoke(TMethod(@t.DoTest).Code, [t], ccReg, Nil, False, False);
  2.  
And now try that same example with TMyObject NOT in the same unit and not accessible for the main program.

Even if DoTest is published/public... is it then still possible to do this with Invoke?
Title: Re: [Solved] RTTI headache
Post by: PascalDragon on February 24, 2020, 02:52:02 pm
Did you read all of my message? You even quoted it: I stressed that retrieving is the problem currently. As long as you can get a pointer to the function or method you can call it, but you need to roll your own way to get the pointer currently.
Title: Re: [Solved] RTTI headache
Post by: rvk on February 24, 2020, 02:58:57 pm
Did you read all of my message? You even quoted it: I stressed that retrieving is the problem currently. As long as you can get a pointer to the function or method you can call it, but you need to roll your own way to get the pointer currently.
Ah, you said:
Quote
It's retrieving non-published functions which is not implemented currently.
So I assumed retrieving public/published functions wouldn't be a problem and wanted to get an example.

But I take it, from your response, you can't get the code-pointer to public functions from an unknown class via RTTI.
(and that was what I was referring to when I said "Getting to a public function is a lot harder.")

B.T.W. If the TMyObject.DoTest is known... just casting it in code would be easier for TS anyway.
Title: Re: [Solved] RTTI headache
Post by: PascalDragon on February 25, 2020, 09:56:06 am
Did you read all of my message? You even quoted it: I stressed that retrieving is the problem currently. As long as you can get a pointer to the function or method you can call it, but you need to roll your own way to get the pointer currently.
Ah, you said:
Quote
It's retrieving non-published functions which is not implemented currently.
So I assumed retrieving public/published functions wouldn't be a problem and wanted to get an example.

Between published and public there is the small, but in this case very important difference of RTTI that is available for the former, but not for the later (at least as of now; that will change with Extended RTTI). So when I say published I really mean published and not public.

But I take it, from your response, you can't get the code-pointer to public functions from an unknown class via RTTI.
(and that was what I was referring to when I said "Getting to a public function is a lot harder.")

But as long as you have some type registry and the method is published one can use that.

B.T.W. If the TMyObject.DoTest is known... just casting it in code would be easier for TS anyway.

Oh definitely. But one might one to prepare the code for what is to come in the future. ;)
TinyPortal © 2005-2018