Re: How can I put the current value of a variable into a delegate?

2024-05-08 Thread Liam McGillivray via Digitalmars-d-learn

On Wednesday, 8 May 2024 at 12:29:05 UTC, Rene Zwanenburg wrote:
Interestingly enough C# used to have the same behaviour but MS 
decided to go for a breaking change in C# 5; now it behaves as 
most people expect.


Wow! I wonder if D would be willing to allow such a breaking 
change with the release of Phobos 3. My choice would be to have 
it use the current value by default for value types, but allow 
them to be linked to the same memory address using `*&` when the 
variable is placed in a delegate. I think that the distinction 
between value types and reference types should be consistent.


If such a breaking change isn't considered acceptable, I suppose 
a new operator can be introduced for dereferencing a variable 
when placed in a delegate. Maybe `#` or `$` if they don't 
conflict with any existing use of those symbols.


Re: How can I put the current value of a variable into a delegate?

2024-05-08 Thread Liam McGillivray via Digitalmars-d-learn

On Monday, 6 May 2024 at 16:41:38 UTC, Steven Schveighoffer wrote:

On Monday, 6 May 2024 at 06:29:49 UTC, Liam McGillivray wrote:
Delegates can be a pain, as they often have results different 
from what one would intuitively expect. This can easily result 
in bugs.


Here's a line that caused a bug that took me awhile to find:
```
foreach(card; unitCards) card.submitted = delegate() => 
selectUnit(card.unit);

```

Each `UnitInfoCard` object (which `card` is a member of) 
contains a `Unit` object called `unit`. The intention of this 
line was that each object in `unitCards` would call 
`selectUnit` with it's own `unit` every time it calls 
`submitted`. Instead, every card calls `submitted` with the 
*last* value of `card`.


Yes, this is because the foreach loop reuses the same memory 
slot for `card`.


Even though this is allocated as a closure, it still only 
allocates the frame stack of the *enclosing function*, and does 
not allocate a new slot for each loop iteration.


You can force this by using a lambda which allocates the 
closure:


```d
foreach(card; unitCards)
card.submitted = (c2) { return () => selectUnit(c2.unit); 
}(card);

```

This is a lambda which accepts `card` as a parameter, and 
returns an appropriate delegate. It is important to use a 
parameter, because if you just use card inside there, it's 
still using the single stack frame of the calling function!


...

I would love to see a solution, but the workaround at least 
exists!


-Steve


Well that's something. It's not a very good solution for a 
language that aims for readability. It took me awhile looking at 
it to figure out what it is about, as I'm not familiar with this 
syntax.


The solution that I did before seeing this was to add a function 
to `UnitInfoCard` to give it a delegate with a `Unit unit` 
parameter, and then that function would give that function with 
the `unit` parameter set to itself to it's own `submitted` 
member. I will probably keep it like this for readability.


```
void clickAction(void delegate(Unit) @safe clickAction) {
submitted = () => clickAction(unit);
}
```


Re: How can I put the current value of a variable into a delegate?

2024-05-08 Thread Rene Zwanenburg via Digitalmars-d-learn

On Monday, 6 May 2024 at 16:41:38 UTC, Steven Schveighoffer wrote:
This is a very old issue: 
https://issues.dlang.org/show_bug.cgi?id=2043 since "moved" to 
https://issues.dlang.org/show_bug.cgi?id=23136


I would love to see a solution, but the workaround at least 
exists!


-Steve


Interestingly enough C# used to have the same behaviour but MS 
decided to go for a breaking change in C# 5; now it behaves as 
most people expect.


Since it's an unsolved problem to keep links working for 10+ 
years I gave up looking for something official about the subject. 
Here's an SO question about it though:


https://stackoverflow.com/questions/14184515/action-delegate-uses-the-last-values-of-variables-declared-outside-foreach-loop




Re: How can I put the current value of a variable into a delegate?

2024-05-06 Thread Steven Schveighoffer via Digitalmars-d-learn

On Monday, 6 May 2024 at 06:29:49 UTC, Liam McGillivray wrote:
Delegates can be a pain, as they often have results different 
from what one would intuitively expect. This can easily result 
in bugs.


Here's a line that caused a bug that took me awhile to find:
```
foreach(card; unitCards) card.submitted = delegate() => 
selectUnit(card.unit);

```

Each `UnitInfoCard` object (which `card` is a member of) 
contains a `Unit` object called `unit`. The intention of this 
line was that each object in `unitCards` would call 
`selectUnit` with it's own `unit` every time it calls 
`submitted`. Instead, every card calls `submitted` with the 
*last* value of `card`.


Yes, this is because the foreach loop reuses the same memory slot 
for `card`.


Even though this is allocated as a closure, it still only 
allocates the frame stack of the *enclosing function*, and does 
not allocate a new slot for each loop iteration.


You can force this by using a lambda which allocates the closure:

```d
foreach(card; unitCards)
card.submitted = (c2) { return () => selectUnit(c2.unit); 
}(card);

```

This is a lambda which accepts `card` as a parameter, and returns 
an appropriate delegate. It is important to use a parameter, 
because if you just use card inside there, it's still using the 
single stack frame of the calling function!


I renamed the inner parameter `c2` to avoid confusion, but you 
could name it `card` also. Essentially, the stack frame of the 
inner function is now allocated a closure, and it has it's own 
reference to `card` as a parameter.


This is a very old issue: 
https://issues.dlang.org/show_bug.cgi?id=2043 since "moved" to 
https://issues.dlang.org/show_bug.cgi?id=23136


I would love to see a solution, but the workaround at least 
exists!


-Steve


Re: How can I put the current value of a variable into a delegate?

2024-05-06 Thread Nick Treleaven via Digitalmars-d-learn

On Monday, 6 May 2024 at 06:29:49 UTC, Liam McGillivray wrote:

Here's a line that caused a bug that took me awhile to find:
```
foreach(card; unitCards) card.submitted = delegate() => 
selectUnit(card.unit);

```


I think you can do:
```d
import std.algorithm.iteration : each;
unitCards.each!(c => c.submitted = () => selectUnit(c.unit));
```



Re: How can I put the current value of a variable into a delegate?

2024-05-06 Thread Nick Treleaven via Digitalmars-d-learn

On Monday, 6 May 2024 at 06:29:49 UTC, Liam McGillivray wrote:
This is because the delegate assignment causes the local `card` 
variable to remain alive. The delegate that's assigned is 
linked to this variable itself, not the value at the time that 
the delegate is assigned.


This is https://issues.dlang.org/show_bug.cgi?id=23136. Perhaps 
it can be fixed in the next edition.


Is there a way I can dereference a variable when placing it in 
a delegate, so that it's current value is used, rather than the 
variable itself?


I think you would need to make an array before the loop, assign 
to an indexed element and use that in the delegate.


How can I put the current value of a variable into a delegate?

2024-05-06 Thread Liam McGillivray via Digitalmars-d-learn
Delegates can be a pain, as they often have results different 
from what one would intuitively expect. This can easily result in 
bugs.


Here's a line that caused a bug that took me awhile to find:
```
foreach(card; unitCards) card.submitted = delegate() => 
selectUnit(card.unit);

```

Each `UnitInfoCard` object (which `card` is a member of) contains 
a `Unit` object called `unit`. The intention of this line was 
that each object in `unitCards` would call `selectUnit` with it's 
own `unit` every time it calls `submitted`. Instead, every card 
calls `submitted` with the *last* value of `card`.


This is because the delegate assignment causes the local `card` 
variable to remain alive. The delegate that's assigned is linked 
to this variable itself, not the value at the time that the 
delegate is assigned.


Is there a way I can dereference a variable when placing it in a 
delegate, so that it's current value is used, rather than the 
variable itself?