Passing method code as an argument as a file

Passing a method as an argument is not a problem:

type TSomething = class Msg: string; procedure Show; end; procedure TSomething.Show; begin ShowMessage(Msg); end; type TProc = procedure of object; procedure Test(Proc: TProc); begin Proc; end; procedure TForm1.Button1Click(Sender: TObject); var Smth: TSomething; begin Smth:= TSomething.Create; Smth.Msg:= 'Hello'; Test(Smth.Show); end; 

I need something complicated - pass only part of the method code. I know I can do this:

 procedure Test2(Code: Pointer); var Smth: TSomething; Meth: TMethod; begin Smth:= TSomething.Create; Smth.Msg:= 'Hello Hack'; Meth.Data:= Smth; Meth.Code:= Code; TProc(Meth); Smth.Free; end; procedure TForm1.Button2Click(Sender: TObject); begin Test2(@TSomething.Show); end; 

but this is hacky and unsafe - the compiler cannot check the arguments of the method.

Question: Is it possible to do the same by type?

+4
source share
4 answers

I finally get it. With type checking, there is no need to declare a variable for the calling event!

 type TSomething = class Msg: string; procedure Show; procedure ShowWithHeader(Header : String); end; TProc = procedure of object; TStringMethod = procedure(S : String) of Object; procedure TSomething.Show; begin ShowMessage(Msg); end; procedure TSomething.ShowWithHeader(Header: String); begin ShowMessage(Header + ' : ' + Msg); end; procedure Test2(Code: TProc); var Smth: TSomething; begin Smth:= TSomething.Create; Smth.Msg:= 'Hello Hack 2'; TMethod(Code).Data := Smth; Code; Smth.Free; end; procedure Test3(Code: TStringMethod; S : String); var Smth: TSomething; begin Smth:= TSomething.Create; Smth.Msg:= 'Hello Hack 3'; TMethod(Code).Data := Smth; Code(S); Smth.Free; end; procedure TForm4.btn1Click(Sender: TObject); begin Test2(TSomething(nil).Show); // Test2(TSomething(nil).ShowWithHeader); // Cannot Compile end; procedure TForm4.btn2Click(Sender: TObject); begin // Test3(TSomething(nil).Show,'Hack Header'); // Cannot Compile Test3(TSomething(nil).ShowWithHeader,'Hack Header'); end; 
+7
source

Finally, I made a workaround based on stub functions. It does not answer my initial question, contains overhead, but solves my problem with duplicate code and exempts from the hacker code:

 type TSmth = class procedure Method1; procedure Method2; end; type TDoMethod = procedure(Instance: TSmth); procedure DoMethod1(Instance: TSmth); begin Instance.Method1; end; procedure DoMethod2(Instance: TSmth); begin Instance.Method2; end; procedure TestMethod(DoMethod: TDoMethod); var Smth: TSmth; begin Smth:= TSmth.Create; { a lot of common setup code here } DoMethod(Smth); { a lot of common check code here } Smth.Free; end; procedure TestMethod1; begin TestMethod(DoMethod1); end; procedure TestMethod2; begin TestMethod(DoMethod2); end; 
+3
source

Disclaimer I personally will never use this code and never recommend using it.

Do it like this:

 procedure Test2(Method: TProc); var Smth: TSomething; begin Smth:= TSomething.Create; Smth.Msg:= 'Hello Hack'; TMethod(Method).Data:= Smth; Method(); end; 

Of course, this is still unsafe, because it will only work if what you put in Data is really compatible with this method.


Sergey asks:

What will you call your Test2 without creating a dummy instance of TSomething?

I suppose you can do it this way for static (i.e. non-virtual and non-dynamic) methods:

 var Obj: TSomething; .... Test2(Obj.Show);//no need to actually create Obj 

Of course, all this illustrates what a grotesque hack is. I think this is no better than the version in your question. There is no real clean way to do what you ask.

I suspect that the right way to solve your real problem would be to use RTTI to call the method.

+2
source

This is an example of using anonymous methods.

No code duplication and type calls.

 type TSmth = class procedure Method1; procedure Method2; end; procedure Test; type TMyMethodRef = reference to procedure; PMyTestRef = reference to procedure(aMethod :TMyMethodRef); var TestP : PMyTestRef; Smth : TSmth; begin TestP := procedure( aMethod : TMyMethodRef) begin Smth := TSmth.Create; try // setup Smth aMethod; // test Smth finally Smth.Free; end; end; TestP(Smth.Method1); // Test Method1 TestP(Smth.Method2); // Test Method2 end; 
+2
source

Source: https://habr.com/ru/post/1401612/


All Articles