Re: Nested delegates and closure allocations

2024-01-17 Thread Anonymouse via Digitalmars-d-learn
On Tuesday, 16 January 2024 at 17:21:12 UTC, FeepingCreature 
wrote:

Correct. [...]


Thanks, I think I understand.


Re: Nested delegates and closure allocations

2024-01-16 Thread FeepingCreature via Digitalmars-d-learn

On Tuesday, 16 January 2024 at 15:39:07 UTC, Anonymouse wrote:
If I make a `scope` variable of the delegate and pass *it* to 
`receiveTimeout`, there no longer seems to be any mention of 
the closure in the error (given 2.092 or later).


```d
void foo(Thing thing) @nogc
{
void sendThing(const string where, int i)
{
send(thing, where, i);
}

scope scopeSendThing = &sendThing;
receiveTimeout(Duration.zero, scopeSendThing);
}
```

Ignoring that it doesn't compile for other reasons; provided 
`scope scopeSendThing = &sendThing;` compiles -- as in, 
`&sendThing` is eligible for `scope` -- is this a valid 
workaround?


Correct. The problem is that `receiveTimeout` is defined as a 
template variadic function: it *can* take a scoped function, but 
it doesn't (can't) declare that its argument is always scoped, so 
since scoped parameters are opt-in, it defaults to unscoped. And 
&sendThing has to also default to unscoped, because you can pass 
unscoped values to scoped parameters but not the other way 
around, so it defaults to the most generic type available. With 
your scoped variable you provide DMD the critical hint that 
actually you want the closure to be scoped, and once the value is 
scoped it stays scoped.


Re: Nested delegates and closure allocations

2024-01-16 Thread Anonymouse via Digitalmars-d-learn
On Tuesday, 16 January 2024 at 13:45:22 UTC, FeepingCreature 
wrote:
Am I safe as long as I don't do something like, pass 
`&sendThing` as an argument to `std.concurrency.receive`?


Yes.


Thank you.

And to make sure I don't misunderstand the spec; in the case I 
*do* have a delegate I want to pass elsewhere, and `scope dg = 
&myFun;` *does* compile, passing that `dg` around won't allocate 
a closure?


```d
void foo(Thing thing) @nogc
{
void sendThing(const string where, int i)
{
send(thing, where, i);
}

receiveTimeout(Duration.zero, &sendThing);
}
```

The above naturally won't compile because 
`std.concurrency.receiveTimeout` requires the garbage collector, 
but notably in the error message, this is included;


```
onlineapp.d(10): Error: function `onlineapp.foo` is `@nogc` yet 
allocates closure for `foo()` with the GC
onlineapp.d(12):`onlineapp.foo.sendThing` closes over 
variable `thing` at onlineapp.d(10)

```

If I make a `scope` variable of the delegate and pass *it* to 
`receiveTimeout`, there no longer seems to be any mention of the 
closure in the error (given 2.092 or later).


```d
void foo(Thing thing) @nogc
{
void sendThing(const string where, int i)
{
send(thing, where, i);
}

scope scopeSendThing = &sendThing;
receiveTimeout(Duration.zero, scopeSendThing);
}
```

Ignoring that it doesn't compile for other reasons; provided 
`scope scopeSendThing = &sendThing;` compiles -- as in, 
`&sendThing` is eligible for `scope` -- is this a valid 
workaround?


Re: Nested delegates and closure allocations

2024-01-16 Thread FeepingCreature via Digitalmars-d-learn

On Tuesday, 16 January 2024 at 10:56:58 UTC, Anonymouse wrote:

I'm increasingly using nested delegates to partition code.

```d
void foo(Thing thing)
{
void sendThing(const string where, int i)
{
send(thing, where, i);
}

sendThing("bar", 42);
}
```

...
3. Those referenced stack variables that make up the closure 
are allocated on the GC heap, unless:


* The closure is passed to a scope parameter.
* The closure is an initializer for a scope variable.
* The closure is assigned to a scope variable.


I'm generally not storing the delegates or passing them around 
as values, so I don't think the thing about scope variables and 
parameters *directly* applies.


Am I safe as long as I don't do something like, pass 
`&sendThing` as an argument to `std.concurrency.receive`?


Yes.