E2555 Cannot capture symbol 'Self'

1.2k Views Asked by At

When I run the following code I get E2555 Cannot capture symbol 'Self'.

type
  TLookupTable = record
    FData: TArray<TOtherRec>;
    procedure ReverseLeftToRight;
  end;

procedure TLookupTable.ReverseLeftToRight;
begin
  Parallel.For(0, Length(FData)-1).NoWait.Execute(procedure(i: integer) begin
    FData[i]:= FData[i].ReverseLeftRight;
  end); {for i}
end;

How do I fix this?

1

There are 1 best solutions below

2
Johan On

The problem is that var parameters (including hidden var parameters to Self) do not get captured. However, we do not want to copy the record, that would be useless, because then our method would not work.

The trick is to make the hidden self parameter explicit.
If it's a class, then it's easy (var S:= Self), if not you'll have to declare a pointer to your record.

procedure TLookupTable.ReverseLeftToRight;
type 
  PLookupTable = ^TLookupTable;
var
  S: PLookupTable;
begin
  S:= @Self;
  Parallel.For(0, Length(FData)-1).NoWait.Execute(procedure(i: integer) begin
    S.FData[i]:= S.FData[i].ReverseLeftRight;
  end); {for i}
end;

Now the compiler no longer complains.
(Note that I'm using the implicit syntax for S^.xyz).

Delphi Rio
Using a inline var declaration as shown below, does not work.

  //S: PLookupTable;
begin
  var S:= @Self; //inline declaration
  Parallel.For(0, Length(FData)-1).NoWait.Execute(procedure(i: integer) begin
    S.FData[i]:= S.FData[i].ReverseLeftRight;
  end); {for i}

This generates: E2018 Record, object or class type required.
I guess the inline @Self gets resolved to a generic pointer, which is a shame, because there is enough info to infer the correct type for the inline variable.

Asynchronous issues
If you're executing the code using a Async (.NoWait) thread/task, then it might be better to put FData in the local variable. FData, being a dynamic array, is already a pointer (so no copying will take place, just a ref count). And dynamic arrays do not have Copy-on-Write semantics, so the original will get updated.
As is, the Self record might go out of scope whilst the code is running, because the pointer operation S:= @Self does not cause the reference count on FData to increase). This might cause an access violation (or worse).
Taking a reference to FData causes its refcount to go up, meaning it cannot go out of scope prematurely.