I'm trying to use an array of Integer parameter inside an anonymous method passed as parameter to another function:

type
  TAnonymousMethod = reference to procedure();

procedure SubTest(AMethod : TAnonymousMethod);
begin
  AMethod();
end;

procedure Test(ACodes : array of Integer);
begin
  SubTest(
    procedure()
    begin
      ShowMessage(IntToStr(Length(ACodes)));
    end
  );
end;

On compiling it produces the following E2555 error:

[dcc32 Error] Unit1.pas(38): E2555 Cannot capture symbol 'ACodes'

I've tried to do the same thing using one only Integer value and it compiled without errors.

procedure Test(ACode : Integer);
begin
  SubTest(
    procedure()
    begin
      ShowMessage(IntToStr(ACode));
    end
  );
end;

So the problem seems to be related to the open array parameters only.

Why does this happen and how could it be avoided?

2

There are 2 best solutions below

1
On

Open arrays are implemented as actually two parameters, the first one being the pointer to the first element, and the second being the highest index.

Knowing this, it becomes clear why such a thing cannot be captured, as the captured value could potentially outlive the original lifetime.

Yes, you can capture other things that potentially outlive their lifetime due to being captured, but in those cases that is because you explicitly destroyed an object for example, or freed some memory, but not because of how it was passed to some routine. Capturing always makes sure that the value is kept alive for at least the same time as the closure lives.

Captured values in a closure are internally implemented as fields in the closure backing object that the compiler creates, and so there is basically an assignment happening from the captured values to these fields (simply said). Open array parameters cannot be assigned to a local variable or similar, only accessed like an array, or passed on further.

0
On

Your anonymous type declaration is wrong. You should declare this type to the procedure takes this parameter type. Like in my example TFoo.It is working:

unit1.pas:

unit Unit3;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
  TFoo = reference to procedure ( ints_ : array of integer );

  TForm3 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
    fFoo : TFoo;
  public
    { Public declarations }
  end;

var
  Form3: TForm3;

implementation

{$R *.dfm}

procedure fooCaller( foo_ : TFoo );
begin
  foo_( [1,2,3] );
end;

procedure fooCaller2( foo_ : TFoo; ints_ : array of integer );
begin
  foo_( ints_ );
end;

procedure TForm3.Button1Click(Sender: TObject);
begin
  fooCaller(
    procedure ( ints_ : array of integer )
    var
      i : integer;
    begin
      i := length( ints_ );
    end );

  // OR  

  fooCaller2(
    procedure ( ints_ : array of integer )
    var
      i : integer;
    begin
      i := length( ints_ );
    end, [1,2,3] );
end;

end.

unit1.dfm:

object Form3: TForm3
  Left = 0
  Top = 0
  Caption = 'Form3'
  ClientHeight = 411
  ClientWidth = 852
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  PixelsPerInch = 96
  TextHeight = 13
  object Button1: TButton
    Left = 288
    Top = 72
    Width = 75
    Height = 25
    Caption = 'Button1'
    TabOrder = 0
    OnClick = Button1Click
  end
end