Am 17.09.2022 um 17:04 schrieb Hairy Pixels:

On Sep 17, 2022, at 9:43 PM, Sven Barth <pascaldra...@googlemail.com> wrote:

Then you didn't read my announcement mail deeply enough, cause it's mentioned 
there!
Yes I just read that over and I remember it now. I feel like this not the same 
behavior in other languages although I’d need to test. Most recently I’m using 
Swift for my day job and I don’t recall ever having confusions like this come 
up.

Complain to the developers of Delphi then.

I do however plan to extend anonymous (and nested) functions with a syntax that allows to select whether a variable should be by-reference or by-value. But that's still a bit off, cause I have other things to do first.


And they are not pointers, but the variable is moved to an object instance 
which implements the interface of the function reference.
“Moved to an object” makes me think the variable is copied but the reference 
behavior you describe sounds like a pointer so I’m still not 100% clear on what 
you mean.

Let's take your initial example and adjust it so that i will really be captured:

=== code begin ===

{$mode objfpc}
{$modeswitch anonymousfunctions}
{$modeswitch functionreferences}
{$modeswitch arrayoperators}

program test;
uses
  Cthreads, SysUtils, Classes;

type
  TProc = reference to procedure;
  TCallback = class(TThread)
    private
      proc: TProc;
    public
      constructor Create(p: TProc);
      procedure Execute; override;
end;

procedure TCallback.Execute;
begin
  proc();
  Sleep(100);
end;

constructor TCallback.Create(p: TProc);
begin
  inherited Create(true);

  proc := p;
end;

procedure DoTest;
var
  i: integer;
  callback: TCallback;
  callbacks: array of TCallback = ();
begin
  for i := 1 to 4 do
    begin
      callback := TCallback.Create(procedure
                  begin
                    writeln('Invoked: ', i, ' id: ', HexStr(TThread.CurrentThread));
                  end);

      callback.Start;
      callbacks += [callback];
    end;

  for i := 0 to High(callbacks) do
    callbacks[i].WaitFor;
end;

begin
  DoTest;
end.

=== code end ===

What the compiler will make of it will be as follows (the only difference will be inside DoTest):

=== code begin ===

procedure DoTest;

type
  TCapturer = class(TInterfacedObject, TProc)
    i: integer;
    procedure Anonymous1;

    procedure TProc.Invoke = Anonymous1;
  end;

  procedure TCapturer.Anonymous1;
  begin
    writeln('Invoked: ', i, ' id: ', HexStr(TThread.CurrentThread));
  end;

var
  capturer: TCapturer;
  capturer_keepalive: IUnknown;
  callback: TCallback;
  callbacks: array of TCallback = ();
begin
  capturer := TCapturer.Create;
  capturer_keepalive := capturer;
  for capturer.i := 1 to 4 do
    begin
      callback := TCallback.Create(capturer as TProc);

      callback.Start;
      callbacks += [callback];
    end;

  for capturer.i := 0 to High(callbacks) do
    callbacks[capturer.i].WaitFor;
end;

=== code end ===

That should make it clearer what is happening behind the scenes.


Even so I’m trying now to modifying the example by copying to a local variable 
but I still see this same behavior suggesting there isn’t actually a copy. Why 
is this?

When your callback is executed i might already have been changed!
Ok that makes sense now since it’s just a reference. So this would have worked 
if I would have passed in i as a parameter and then stored it locally? I’m 
thinking now how I can preserve a copy of the state in the scope for each 
thread and I think local vars are the only way, correct? We need some more 
thread based examples to help people understand I think because I’m finding all 
sorts of edge cases updating a little thread library myself.

You need to move the creation of the thread to a separate nested function (cause each thread will then have a separate capture object; also this will work no matter if this is done in the main block or inside a procedure/function/method):

=== code begin ===

function CreateCallback(aIndex: Integer): TCallback;
begin
    Result := TCallback.Create(procedure
                begin
                  writeln('Invoked: ', aIndex, ' id: ', HexStr(TThread.CurrentThread));
                end);
end;

var
  i: integer;
  callback: TCallback;
  callbacks: array of TCallback = ();
begin
  for i := 1 to 4 do
    begin
      callback := CreateCallback(i);

      callback.Start;
      callbacks += [callback];
    end;

  for i := 0 to High(callbacks) do
    callbacks[i].WaitFor;
end.

=== code end ===

Regards,
Sven
_______________________________________________
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal

Reply via email to