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?
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With