8

hi all :) it is my first question at stackoverflow :)

in Delphi XE2 RTTI we have TRttiMethod class and it has function CreateImplementation() wich allows to dynamically create procedure or function with the same signature and a anonimous method as its body.

using TRttiContext.getMethods()/getMethod() we can get collection of class methods - collection of TRttiMethod.

documentation says (http://docwiki.embarcadero.com/VCL/en/RTTI.TRttiMethod) do not to create TRTTiMethod directly, and use getMethods() to get its instances.

so, if we have TRTTIMethod instance, we can dynamically create method with the same signature and invoke it later.

the question is.. for example, we have TNotifyEvent... TRttiContext.getType(typeinfo(TNotifyEvent)) returns TRttiType instance object wich has typekind = tkMethod. we know that signature of TNotifyEvent is procedure (sender : TObject) of object;

is it possible to get TRttiMethod for this? I want to dynamically create eventHandler for the event using TRttiMethod.CreateImplementation() or may be there is another way to dynamically create method implementation?

PS: idea is to create something like Event Chain. At compile time we don't know event type/signature, so we can use generics for this like TEvenChain<TNotifyEvent>. Chain has collection on registered event handlers, so because we don't know handler type at compile time we have to create it dynamically, and that handler only gets its parameters and calls registered handler with them.

update: here is some code (idea is to create event handlers chain):

procedure TMainForm.FormCreate(Sender: TObject);
begin
    FEventChain := TNotifyChain.Create();
    FEventChain.AddHandler(event1);
    FEventChain.AddHandler(event2);   

    TestButton.OnClick := FEventChain.EventHandler;
end;

Event1 & event2 are methods(sender : TObject) of form; it is not generic example (T in this case is NotifyEvent/TChain<TNotifyEvent>) TEventChain.EventHandler is TNotifyEvent too

TNotifyChain is

TNotifyChain = class(TObject)
  strict private
    FEvent : TNotifyEvent;
    FItems : TList<TNotifyEvent>;

    FCtx : TRttiContext;
  public
    procedure TestNotifyHandler(sender : TObject);   virtual; abstract;

    constructor Create();
    procedure AddHandler(eh : TNotifyEvent);
    property  EventHandler : TNotifyEvent read FEvent;
end;

EventHandler property is mapped to FEvent variable. And FEvent is not assigned as we assume that type of event/handler is unknown.

so in constructor we create virtual method with TNotifyEvent signature and assign its code to FEvent:

constructor TNotifyChain.Create();
var st: TRttiType;
    //et : TRttiMethodType;
    Callback : TMethodImplementationCallback;
    rttiMethod : TRttiMethod;
    m : TMethod;
    mi : TMethodImplementation;
begin
    inherited;
    FItems := TList<TNotifyEvent>.Create();

    FCtx := TRttiContext.Create();

    //et := FCtx.GetType(typeinfo(TNotifyEvent)) as TRttiMethodType;
    st := FCtx.GetType(self.ClassType);


    rttiMethod := st.GetMethod('TestNotifyHandler');

    Callback := procedure(UserData: Pointer;
                        const Args: TArray<TValue>;
                        out Result: TValue)
             var i : integer;
                 e : TMethod;
             begin

                for i := 0 to FItems.Count - 1 do begin
                    e := TMethod(Fitems[i]);
                    result := Invoke(e.Code, args, rttiMethod.CallingConvention, nil);
                end;
             end;


    mi := rttiMethod.CreateImplementation(self, Callback);

    m.data := self;
    m.Code := mi.CodeAddress;
    FEvent := TNotifyEvent(m);
end;

here I use TestNotifyHandler method to create actual handler with the same signature using TRttimethod.CreateImplementation()

so I suppose, there is a way to create implementation of event handler at runtime using TRttiMethodType from TNotifyEvent because it has info about params type/count and calling convention of actual event used (in generic case).

dummzeuch
  • 10,975
  • 4
  • 51
  • 158
teran
  • 3,214
  • 1
  • 21
  • 30
  • How far did you get at now, please share. – menjaraz Apr 26 '12 at 08:28
  • @menjaraz I haven't solved this problem. It wasn't "real" task for me, "just for fun". I wrote article about this in my blog: http://teran.karelia.pro/articles/item_4508.html (but it is originally in russian); you can use Google to translate it to english [link]http://translate.google.com/translate?sl=ru&tl=en&js=n&prev=_t&hl=ru&ie=UTF-8&layout=2&eotf=1&u=http%3A%2F%2Fteran.karelia.pro%2Farticles%2Fitem_4508.html&act=url – teran Apr 26 '12 at 09:17
  • @menjaraz you are welcome. Google has some problems with source code in my article (because of SyntaxHighlight), so see souce code in original article. In the end my solution was to create separate child classes for each event type chain. but these child classes are abstract, and they have only one abstract method, wich we can use to create MethodImplementation using `TRttiMethod` – teran Apr 26 '12 at 09:32
  • I have noticed that. After all Delphi codes remain the same whatever the language of the blog. I am interested in all your RTTI related posts. Thank you again. – menjaraz Apr 26 '12 at 09:49

1 Answers1

5

A TRttiType with typekind TkMethod is going to be represented as a TRttiMethodType. You can't get a TRttiMethod from it, because it's not a method; it's a method pointer, but if you take a look at TRttiMethodType and its Invoke method, you should find exactly what you need.

Mason Wheeler
  • 82,511
  • 50
  • 270
  • 477
  • thanks for reply. i'm not shure that `TRttiMethodType.Invoke` is what I need; because it is used to invoke actually existing methods of object. In my case there is no such method and I have to create it before. I've added some source of my `TNotifyChain` class to demonstrate the problem. – teran Feb 17 '12 at 17:21