pyscripter 689 Posted July 17, 2022 (edited) System.Rtti contains a lesser known, but powerful class TMethodImplementation, which is basically hidden and used in TVirtualMethodInterceptor. Outside this use, it cannot be created directly and it can only be accessed by TRttiMethod.CreateImplementation. I recently discovered that Spring4d extends its use through a helper for TRttiInvokableType, so it can be used with standalone procedures and functions as well as with events. Here is a small console app showing how it can be used: program MethodImplementationTest; {$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils, System.TypInfo, System.Rtti, Spring; type TSimpleProc = function(I: Integer): Integer; procedure ImplCallback(UserData: Pointer; const Args: TArray<TValue>; out Result: TValue); begin WriteLn('From Implementation'); Result := Args[0].AsInteger * 2; end; var Proc: TSimpleProc; begin ReportMemoryLeaksOnShutdown := True; try var RTTIContext := TRttiContext.Create; var RTTIType := RTTIContext.GetType(TypeInfo(TSimpleProc)); var ProcImplementation := (RTTIType as TRttiInvokableType).CreateImplementation(nil, ImplCallback); Proc := TSimpleProc(ProcImplementation.CodeAddress); WriteLn(Proc(2)); ProcImplementation.Free; except on E: Exception do Writeln(E.ClassName, ': ', E.Message); end; ReadLn; end. Output: From Implementation 4 Simple and easy. Spring4D is full of gems. I wish the above functionality was part of the standard library. I can think of many uses: Python4Delphi contains a unit MethodCallback which uses complex assembly to convert methods into stubs that can be used with external C libraries. It could be rewritten using TMethodImplementation, without assembly code. Also in Python4Delphi, the implementation of Delphi events using python code could be very much simplified and generalized using TMethodImplementation. You can create Delphi functions, procedures and methods, event handlers on the fly that are implemented by python code. Similarly functionality can be provided with other scripting languages. Edited July 18, 2022 by pyscripter 2 Share this post Link to post
pyscripter 689 Posted July 17, 2022 Feature request submitted: [RSP-38690] Add CreateImplementation method for TRttiInvokableType - Embarcadero Technologies Share this post Link to post
Stefan Glienke 2002 Posted July 18, 2022 FWIW I only use this to make my multicast events work on platforms where I don't have exactly that: handcrafted assembly code to do the parameter passing. And these run hundreds of times faster than using RTTI. You can turn on using RTTI instead of the asm code in Spring.Events Here is a simple benchmark result: asm (win32) Event invokes/ms: 14925 rtti Event invokes/ms: 205 I have already started digging more into the TMethodImplementation class to optimize its use for those platforms where I don't have the handcrafted asm. There are also libraries that avoid the overhead of the Delphi RTTI to do dynamic invocation by handling all the low level details: https://github.com/d-mozulyov/Tiny.Library#rtti but that requires doing the parameter passing via some stubs written in c asm 1 1 Share this post Link to post
pyscripter 689 Posted July 18, 2022 (edited) Here is an example of making an event handler on the fly using spring4d, TMethodImplementation and a callback routine, in case anyone has a use for it. program MethodImplementationTest2; {$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils, System.TypInfo, System.Rtti, Spring; type TSimpleEvent = function(I: Integer): Integer of object; TTestClass = class private fEvent: TSimpleEvent; published property Event: TSimpleEvent read fEvent write fEvent; end; procedure ImplCallback(UserData: Pointer; const Args: TArray<TValue>; out Result: TValue); begin WriteLn('From Implementation'); WriteLn(Args[0].AsObject.ClassName); Result := Args[1].AsInteger * 2; end; begin ReportMemoryLeaksOnShutdown := True; var TestObj := Shared.Make(TTestClass.Create)(); try var RTTIContext := TRttiContext.Create; var RTTIType := RTTIContext.GetType(TypeInfo(TTestClass)); var RTTIMethodType := RTTIType.GetProperty('Event').PropertyType; var MethodImplementation := (RTTIMethodType as TRttiInvokableType).CreateImplementation(nil, ImplCallback); var Method: TMethod; Method.Code := MethodImplementation.CodeAddress; Method.Data := TestObj; SetMethodProp(TestObj, 'Event', Method); WriteLn(TestObj.Event(2)); MethodImplementation.Free; except on E: Exception do Writeln(E.ClassName, ': ', E.Message); end; ReadLn; end. Output: From Implementation TTestClass 4 Edited July 18, 2022 by pyscripter Share this post Link to post
Dave Novo 51 Posted July 18, 2022 (edited) Hello, While technically this seems cool, why is this particularly useful? I see that I can create an event handler without an explicit object that will implement the event handler method. Why would I need to do this? If I needed this, would I not simply design my class to accept anonymous methods? Or is this for a trick to allow classes not designed to handle anonymous methods to actually handle them? Edited July 18, 2022 by Dave Novo Share this post Link to post
pyscripter 689 Posted July 19, 2022 (edited) 1 hour ago, Dave Novo said: While technically this seems cool, why is this particularly useful? I see that I can create an event handler without an explicit object that will implement the event handler method. Why would I need to do this? Did you read my first post? It answers your question providing some use cases. Spring4d also uses this for supporting multicast events. Edited July 19, 2022 by pyscripter Share this post Link to post
Dave Novo 51 Posted July 19, 2022 Of course I read your post. But I am unclear what could not be handled using anonymous methods? i.e. while the code in procedure ImplCallback(UserData: Pointer; const Args: TArray<TValue>; out Result: TValue); has a very general method signature, in actuality every implementation has to know exactly how many args are going to come in the args array, and exactly what they are going to mean. So why can you not wrap the python code in an anonymous method and then pass the anon method to delphi. I am unclear of what the example of why you would need a method stub. Share this post Link to post
pyscripter 689 Posted July 19, 2022 (edited) In using delphivcl.pyd the user can attach an arbitrary python method as an event handler to any vcl component at runtime. The event handler needs to be created on-the-fly, since you do not know in advance what python method will attach to which events. TMethodImplementation can do just that. Create an event handler of the right type when it is need. In this use case the callback would translate the Args to their python equivalents and call the python code. The callback can be an anonymous method, but this is not important. The important thing is that the event handler needs to be created at runtime and not at compile time. Currently python4delphi needs to provide helper classes for each event type (TNotifyEventHandler for TNotifyEvent etc.). All this code can go away and have just one generic implementation of event handling that can deal with any kind of event. For example in the code below: class MyForm(Form): def __init__(self, owner): self.timer = Timer(self) self.timer.Interval = 10 self.timer.OnTimer = self.__on_timer self.timer.Enabled = True def __on_timer(self, sender): self.CheckBox1.Enabled = True when self.timer.OnTimer = self.__on_timer is executed an event handler needs to be attached to the OnTimer event of a Delphi timer object that will run the python method. Edited July 19, 2022 by pyscripter Share this post Link to post