15

If I try to use a closure on an event handler the compiler complains with :

Incompatible types: "method pointer and regular procedure"

which I understand.. but is there a way to use a clouser on method pointers? and how to define if can?

eg :

Button1.Onclick = procedure( sender : tobject ) begin ... end;

Thanks!

Wouter van Nifterick
  • 23,603
  • 7
  • 78
  • 122
MrMahgu
  • 444
  • 6
  • 12

4 Answers4

10
@Button1.OnClick := pPointer(Cardinal(pPointer( procedure (sender: tObject) 
begin 
  ((sender as TButton).Owner as TForm).Caption := 'Freedom to anonymous methods!' 

end )^ ) + $0C)^;

works in Delphi 2010

Toby Allen
  • 10,997
  • 11
  • 73
  • 124
Codenoid
  • 116
  • 1
  • 2
  • 1
    @Majin: this is clever and it works because an anonymous method is embedded in an implicit class. Be sure to read the comment from Barry Kelly in the referenced article on future compatibility, and this post by Barry Kelly: http://blog.barrkel.com/2010/01/using-anonymous-methods-in-method.html – Jeroen Wiert Pluimers Apr 13 '11 at 14:25
  • 2
    This solution works on Win32 platform but not Win64. – Chau Chee Yang Jul 02 '14 at 06:02
5

An excellent question.

As far as I know, it's not possible to do in current version of Delphi. This is much unfortunate since those anonymous procedures would be great to have for quickly setting up an object's event handlers, for example when setting up test fixtures in a xUnit kind of automatic testing framework.

There should be two ways for CodeGear to implement this feature:

1: Allow for creation of anonymous methods. Something like this:

Button1.OnClick := procedure( sender : tobject ) of object begin
  ...
end;

The problem here is what to put as the self pointer for the anonymous method. One might use the self pointer of the object from which the anonymous method was created, but then one can only create anonymous methods from an object context. A better idea might be to simply create a dummy object behind the scenes to contain the anonymous method.

2: Alternatively, one could allow Event types to accept both methods and procedures, as long as they share the defined signature. In that way you could create the event handler the way you want:

Button1.OnClick := procedure( sender : tobject ) begin
  ...
end;

In my eyes this is the best solution.

Hans-Eric
  • 140
  • 7
  • A method doesn't have the same calling signature as a procedure with the same arguments. Methods always pass Self as a "hidden" argument. – Mason Wheeler Dec 25 '08 at 17:48
  • 1
    Yes, of course. But I see no reason why the compiler shouldn't be able to handle both cases "behind the scenes". It could for instance create a dummy class wrapper around the anonymous procedure if a method is expected. – Hans-Eric Feb 03 '09 at 08:29
  • 1
    This pattern is so common in dynamic languages such as JavaScript Python. – Edwin Yip Oct 22 '13 at 08:41
4

In previous Delphi versions you could use a regular procedure as event handler by adding the hidden self pointer to the parameters and hard typecast it:

procedure MyFakeMethod(_self: pointer; _Sender: TObject);
begin
  // do not access _self here! It is not valid
  ...
end;

...

var
  Meth: TMethod;
begin
  Meth.Data := nil;
  Meth.Code := @MyFakeMethod;
  Button1.OnClick := TNotifyEvent(Meth);
end;

I am not sure the above really compiles but it should give you the general idea. I have done this previously and it worked for regular procedures. Since I don't know what code the compiler generates for closures, I cannot say whether this will work for them.

dummzeuch
  • 10,975
  • 4
  • 51
  • 158
2

Its easy to extend the below to handle more form event types.

Usage

procedure TForm36.Button2Click(Sender: TObject);
var
  Win: TForm;
begin
  Win:= TForm.Create(Self);
  Win.OnClick:= TEventComponent.NotifyEvent(Win, procedure begin ShowMessage('Hello'); Win.Free; end);
  Win.Show;
end;

Code

unit AnonEvents;

interface
uses
  SysUtils, Classes;

type
  TEventComponent = class(TComponent)
  protected
    FAnon: TProc;
    procedure Notify(Sender: TObject);
    class function MakeComponent(const AOwner: TComponent; const AProc: TProc): TEventComponent;
  public
    class function NotifyEvent(const AOwner: TComponent; const AProc: TProc): TNotifyEvent;
  end;

implementation

{ TEventComponent }

class function TEventComponent.MakeComponent(const AOwner: TComponent;
  const AProc: TProc): TEventComponent;
begin
  Result:= TEventComponent.Create(AOwner);
  Result.FAnon:= AProc;
end;

procedure TEventComponent.Notify(Sender: TObject);
begin
  FAnon();
end;

class function TEventComponent.NotifyEvent(const AOwner: TComponent;
  const AProc: TProc): TNotifyEvent;
begin
  Result:= MakeComponent(AOwner, AProc).Notify;
end;

end.
William Egge
  • 429
  • 4
  • 5