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

Liam McGillivray yoshi.pit.link.mario at gmail.com
Wed May 8 22:10:23 UTC 2024


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);
     }
```


More information about the Digitalmars-d-learn mailing list