Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

FreePascal RTTI. Is there a way to invoke method?

I'm trying to find out if there is a way to do things similar to Delphi's enhanced RTTI features.

As far as I know FPC doesn't provide RTTI features which appeared in Delphi since Delphi 2010. But I'd like to find some way to do a few tricks during runtime.

Using typinfo unit in FPC I can do such things as:

  • get Object published property list - via getPropList from typinfo unit;
  • get/set value of the Object's published property - via GetPropValue(...): Variant/SetPropValue(...Value: Variant);
  • get published method - via MethodAddres;

But I haven't found a way to do things like:

  • call methods;
  • call constructors, or create Objects;

Update: the problem with constructors is much like methods one - I want to have a way to pass different params in it:

// concept of code
type

TClass = class of TObject;

TMyClass1 = class
  public
    constructor Create(Param1: Integer; Param2: string); override;
  end;

TMyClass2 = class
  public
    constructor Create(ObjectParam: Object); override;
  end;

TParams = array of Variant;

var 
Classes: array of TClass 
Instances: array of Object;
ParamArray: array of TParams;

... 

For I := 0 to Count-1 do 
begin
  LocalConstructor := @(Classes[I].Create);
  Instances[I] := CallConstructor(LocalConstructor, ParamArray[I]);
end;

So I need to call constructor without knowing its signature.

So my problem is to call an Object's method and pass some parameters to it. It could look like function CallMethod(Instance: Object; MethodName: String; Params: array of Variant): Variant;

If I'm not mistaken it could be solved via Delphi's 2010+ RTTI. But before using enhanced Delphi's RTTI I'd like to understand is it possible in FPC.

In other words my current problem is pass arguments to a routine. I know it can be done using this scheme:

type
  TmyProc = procedure CallMe(x: byte);
...
  var proc: TmyProc;
...
  proc := pointerToFunc^;
  proc(0);

But I need to implement it without knowing count and types of parameters (during compile time).

There are a few links related to the topic:

Delphi: Call a function whose name is stored in a string

http://www.swissdelphicenter.ch/torry/showcode.php?id=1745

The second article (http://www.swissdelphicenter.ch/torry/showcode.php?id=1745) describes a way to pass arguments to a routine imported from DLL by name. Which is almost that I need I suppose. But I'm not sure that way is reliable.

Maybe there's any library, which implements these things using "old" typinfo unit (without RTTI unit)?

Also I'm interested in a way to create some kind of universal event handlers - procedures which can be assigned to different events (with different sets of parameters) e.g.:

procedure myEventHandler(params: array of variant);
...
Button.OnClick := myEventHandler;
Button.OnMouseMove := myEventHandler;

is this possible? Or at least something similar to it?

like image 595
Int0h Avatar asked Mar 23 '16 20:03

Int0h


1 Answers

  1. You can call methods, even not published, using MethodAddress, but it's up to you to ensure correct argument list.
  2. You can call constructors using metaclasses (class references), example of it could be seen in TCollection: you pass class of your collection item at runtime and then it can be created when needed. By defining abstract class with virtual (and probably abstract) constructor, you can come up with argument list you wish, some example here.
  3. AFAIK there is no way to determine argument list at runtime, but if you design both the methods to call and caller itself, there are many ways you can implement similar behavior.

For example, you pass variant open array (Array of const), as it's done in Format(), so number of arguments and its type may vary. But even having one and only pointer as the argument, you sure can pass as many as you want, all you need to do is to come up with some class to which it will lead.

like image 170
Yuriy Afanasenkov Avatar answered Oct 15 '22 22:10

Yuriy Afanasenkov