Bookstore

Recent

Author Topic: [Solved] RTTI headache  (Read 639 times)

mtournay

  • Jr. Member
  • **
  • Posts: 58
[Solved] RTTI headache
« 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 !
« Last Edit: February 21, 2020, 08:11:26 am by mtournay »
laz 2.06 32b - fpc 3.04 32b - win10 64b

korba812

  • Full Member
  • ***
  • Posts: 118
Re: RTTI headache
« Reply #1 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.  

Thaddy

  • Hero Member
  • *****
  • Posts: 9782
Re: RTTI headache
« Reply #2 on: February 19, 2020, 12:30:57 pm »
did you try trunk? and declare the classes with {$M+}?
I am more like donkey than shrek

howardpc

  • Hero Member
  • *****
  • Posts: 3355
Re: RTTI headache
« Reply #3 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.

Thaddy

  • Hero Member
  • *****
  • Posts: 9782
Re: RTTI headache
« Reply #4 on: February 19, 2020, 12:56:17 pm »
extended rtti extends beyond published
I am more like donkey than shrek

mtournay

  • Jr. Member
  • **
  • Posts: 58
Re: RTTI headache
« Reply #5 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 )
laz 2.06 32b - fpc 3.04 32b - win10 64b

rvk

  • Hero Member
  • *****
  • Posts: 3943
Re: RTTI headache
« Reply #6 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;

mtournay

  • Jr. Member
  • **
  • Posts: 58
Re: RTTI headache
« Reply #7 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 !
laz 2.06 32b - fpc 3.04 32b - win10 64b

PascalDragon

  • Hero Member
  • *****
  • Posts: 1075
  • Compiler Developer
Re: RTTI headache
« Reply #8 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.

rvk

  • Hero Member
  • *****
  • Posts: 3943
Re: RTTI headache
« Reply #9 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) yet.
At least not like Delphi.

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


Thaddy

  • Hero Member
  • *****
  • Posts: 9782
Re: RTTI headache
« Reply #10 on: February 20, 2020, 11:52:22 am »
I see FPC hasn't implemented an ObjectInvoke (ObjAuto module) 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.
I am more like donkey than shrek

PascalDragon

  • Hero Member
  • *****
  • Posts: 1075
  • Compiler Developer
Re: [Solved] RTTI headache
« Reply #11 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) 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.
« Last Edit: February 24, 2020, 02:44:57 pm by PascalDragon »

rvk

  • Hero Member
  • *****
  • Posts: 3943
Re: [Solved] RTTI headache
« Reply #12 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?

PascalDragon

  • Hero Member
  • *****
  • Posts: 1075
  • Compiler Developer
Re: [Solved] RTTI headache
« Reply #13 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.

rvk

  • Hero Member
  • *****
  • Posts: 3943
Re: [Solved] RTTI headache
« Reply #14 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.