Hairy Pixels wrote 5 lines
> On Sep 17, 2022, at 9:43 PM, Sven Barth 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.
Show quoted text 5 lines
>
> 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