Anonymous methods in Delphi: the internals

2

Barry Kelly has explained in his recent post how to obtain a method pointer from an anonymous method. Here I will play a little more with the anonymous methods (using Delphi 2009) and consider a somewhat reversed problem – how to create an anonymous method that incapsulates the ordinary function.

First how it is done in the “normal” way:

type
  TIntFunc = function(Value: Integer): Integer;
  TIntFuncRef = reference to function(Value: Integer): Integer;

function Square(Value: Integer): Integer;
begin
  Result:= Value * Value;
end;

function MakeAnonMethod(AFunc: TIntFunc): TIntFuncRef;
begin
  Result:= function(Value: Integer): Integer
  begin
    Result:= AFunc(Value);
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  AnonMethod: TIntFuncRef;

begin
  AnonMethod:= MakeAnonMethod(@Square);
  ShowMessage(IntToStr(AnonMethod(2)));
end;

The MakeAnonMethod function creates the anonymous method that captures a function variable (AFunc argument, actually a pointer). That is simple.

Now let us consider what is going on behind the scene. An anonymous method is internally an interface with a single method “Invoke”:

type
  TAnonCaller = class(TInterfacedObject, TIntFuncRef)
  private
    FFunc: TIntFunc;
    function Invoke(Value: Integer): Integer;
  public
    constructor Create(AFunc: TIntFunc);
  end;

constructor TAnonCaller.Create(AFunc: TIntFunc);
begin
  FFunc:= AFunc;
end;

function TAnonCaller.Invoke(Value: Integer): Integer;
begin
  Result:= FFunc(Value);
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  AnonMethod: TIntFuncRef;

begin
  AnonMethod:= TAnonCaller.Create(@Square);
  ShowMessage(IntToStr(AnonMethod(2)));
end;

The second code snippet do the same thing as the first – it “captures” the function variable in TAnonCaller.Create constructor and uses it to call the “captured” function.
The above code snippets show that

  • when we create an anonymous method we actually create an object that implements an interface of a special kind and obtain a reference to the interface
  • when we call the anonymous method we actually call the Invoke method of the interface.

The above is an oversimplified description of how the anonymous methods work internally. A more closer look at anonymous methods shows that several anonymous methods may share a single object implementing the interfaces for all of them, and that is the way how the different anonymous methods share the captured variables (as fields of the common object).

Advertisements

Delphi 2009 IDE Crash

0

I have encountered an interesting case of Delphi 2009 IDE (12.0.3420.21218, all updates installed) crash today. I just edited a code unit in Delphi IDE and pasted the code fragment written in built-in asm without asm..end directives. After a while the IDE stopped to react on my actions with 98% CPU load. I terminated the Delphi application from the task manager and restarted it. I did the same copy-paste action and about 10 seconds later IDE halted with “access violation” message box. I restarted Delphi again, opened the project, pasted asm code fragment, commented {..} the fragment quickly and saved the unit. The problem was solved.
Evidently the Delphi IDE tried to parse the pasted fragment in the background and failed because of some fatal IDE Editor bug.