[fpc-pascal] Are there any drawbacks to "reference to"?

Anthony Walter sysrpl at gmail.com
Sun Jun 5 23:46:43 CEST 2022


Thanks for that, but is that true for all conditions. I remember reading
and tinkering about with Delphi when it first introduced closures. At the
time it made sense that the extra cruft and interface were needed to
capture local state, but I was under the impression that the interface was
only needed when a local state existed. In the case where the assigned
method or procedure is not a closure an interface doesn't need to exist.

Consider the following bit of code:

type
  TMultiply = reference to function(Value: Integer): Integer;

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

function MultiplyClosure(const S: string): TMultiply;
var
  I: Integer;
begin
  I := StrToInt(S);
  Exit(function(Value: Integer): Integer
  begin
    Result := I * Value;
  end);
end;

{ TForm1 }

function TForm1.MultiplyMethod(Value: Integer): Integer;
begin
  Result := Value * Tag;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  M: TMultiply;
begin
  case Tag of
    0: M := MultiplyFunc;
    1: M := MultiplyClosure('15');
  else
    M := MultiplyMethod;
  end;
  WriteLn(M(4));
  Tag := Tag + 1;
end;

In the situation of "M" being assigned a reference to MultiplyFunc no local
state information needs to be captured inside MultiplyFunc. There will
never be a need to create or initialize an interface for the purpose of
capturing state. The underlying compiler code ought to be able to discard
any code generating an interface and simply call MultiplyFunc.

In the situation of "M" being assigned a reference to MultiplyClosure the
local variable "I" needs to be captured because it makes use of an
anonymous function. As such, there is a need for a mechanism to
capture this state. If the underlying compiler code uses a reference
counted interface to accomplish this, then that is an
understandable penalty because some capture mechanism is necessary.

In the situation of "M" being assigned a reference to MultiplyMethod again
no local state information needs to be captured inside MultiplyMethod. The
reference to Self is passed to the method as a hidden parameter each time
it is invoked. "M" can a simple two pointer data structure (see TMethod),
and no closure (or interface mechanism) is needed.

So in summary, some mechanism, such as a reference counted interface, is
only needed when a value of type TMultiply is assigned to an anonymous
function (a closure). Of course code that invokes a TMultiply would need to
know when to use any of these possibilities (a function, method, or closure
reference). I imagine detection of this possibility could be simply
implemented as a small data structure, thereby saving a lot of overhead and
or complexity in situations where TMultiply uses a closure reference
infrequently:

TReferenceTo = record
  Kind: Integer; // 0 = procedure/function, 1 = method, 2 = closure
  case Integer of
    0: (Code: Pointer);
    1: (Method: TMethod);
    2: (Intf: IInterface);
  end;

Additional note: The most recent trunk revision of fpc from gitlab has a
major bug with either

{$modeswitch functionreferences}
or
{$modeswitch anonymousfunctions}

I will follow up with more information about that bug in a different thread.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.freepascal.org/pipermail/fpc-pascal/attachments/20220605/2fe6eece/attachment.htm>


More information about the fpc-pascal mailing list