Thread overview
callback craziness
Aug 07, 2016
Engine Machine
Aug 07, 2016
ag0aep6g
Aug 07, 2016
Engine Machine
Aug 07, 2016
ag0aep6g
Aug 07, 2016
Adam D. Ruppe
Aug 08, 2016
Engine Machine
Aug 08, 2016
ag0aep6g
August 07, 2016
I use callbacks a lot and have trouble with D in a nogc context.

First, I cannot declare the parameters with a nogc or I get a compile time error.

@nogc void foo(void delegate(int x) @nogc f);

fails with the @nogc.

2nd, I cannot use a delegate because of the @nogc context,

@nogc void foo(void function(int x) f);

Works, but f then cannot access the context.

So, to get around these problems, I have to do something like this:

alias callback(Args) = @nogc void function(int x, Args);
@nogc void foo(Args...)(callback!Args f, auto ref Args args, int extra = 0)

The problem with this is that I can't seem to add f inline:

foo!string((int x, string s) { }, 1);

this fails with template mismatch.

But if I define the lambda outside it works:

auto f = (int x, string s) { };
foo!string(f, 1);

The problem with this is that when I want to pass no arguments,

auto f = (int x) { };
foo(f, 1);

fails. It seems that Args... requires at least one argument to match the template? This may be a bug?


alias callback(Args) = @nogc void function(string, int, Args);
@nogc public void foo(Args...)(callback!Args c, auto ref Args args, int extra = 0)
{
   ...
}
		
auto f = (string s, int l) @nogc
{
    printf("%s\n", s.ptr);
};
foo(f, 1);

or

auto f = (string s, int l, string x) @nogc
{
    printf("%s\n", x.ptr);
};
foo!string(f, "Test string", 1);

which is the case that work sin mine code. But I don't always want to have to pass stuff.

Any ideas? I realize the code is messy. You'll have to read between the lines.



So, the two problems I have is that I would like to be able to add a @nogc callback inline and to be able to pass no arguments.


August 07, 2016
On 08/07/2016 10:01 PM, Engine Machine wrote:
> @nogc void foo(void delegate(int x) @nogc f);
>
> fails with the @nogc.

Compiles just fine for me.

> 2nd, I cannot use a delegate because of the @nogc context,

Delegates don't necessarily need a GC allocation. They only need it when they need a closure. Delegates of methods don't need closures. And when you pass the delegate in a `scope` parameter, no closure is needed, either.

[...]
> So, to get around these problems, I have to do something like this:
>
> alias callback(Args) = @nogc void function(int x, Args);
> @nogc void foo(Args...)(callback!Args f, auto ref Args args, int extra = 0)
>
> The problem with this is that I can't seem to add f inline:
>
> foo!string((int x, string s) { }, 1);
>
> this fails with template mismatch.

You're missing an argument there. The second parameter of foo is `args` which is `string` here. This call works:

    foo!string((int x, string s) { }, "", 1);

> But if I define the lambda outside it works:
>
> auto f = (int x, string s) { };
> foo!string(f, 1);

Doesn't work for me. Still missing the string argument.

> The problem with this is that when I want to pass no arguments,
>
> auto f = (int x) { };
> foo(f, 1);
>
> fails. It seems that Args... requires at least one argument to match the
> template? This may be a bug?

One thing you need to fix: The `callback` template needs a template sequence parameter (i.e. `Args...`). Otherwise it takes exactly one type.

That doesn't make it work, though. You also need to add empty template instantiation parentheses (i.e. `foo!()`), and you need to remove `auto ref` from the `args` parameter.

No idea why it doesn't work with `auto ref`. At least that part looks like a bug to me.
August 07, 2016
On Sunday, 7 August 2016 at 20:48:29 UTC, ag0aep6g wrote:
> On 08/07/2016 10:01 PM, Engine Machine wrote:
>> @nogc void foo(void delegate(int x) @nogc f);
>>
>> fails with the @nogc.
>
> Compiles just fine for me.
>
>> 2nd, I cannot use a delegate because of the @nogc context,
>
> Delegates don't necessarily need a GC allocation. They only need it when they need a closure. Delegates of methods don't need closures. And when you pass the delegate in a `scope` parameter, no closure is needed, either.

Well, one can't pick the case the delegate is passed. When I use a delegate in the nogc context it errs.

> [...]
>> So, to get around these problems, I have to do something like this:
>>
>> alias callback(Args) = @nogc void function(int x, Args);
>> @nogc void foo(Args...)(callback!Args f, auto ref Args args, int extra = 0)
>>
>> The problem with this is that I can't seem to add f inline:
>>
>> foo!string((int x, string s) { }, 1);
>>
>> this fails with template mismatch.
>
> You're missing an argument there. The second parameter of foo is `args` which is `string` here. This call works:
>
>     foo!string((int x, string s) { }, "", 1);
>

Yeah, that was just a typeo obvious. That's not the reason it fails.

>> But if I define the lambda outside it works:
>>
>> auto f = (int x, string s) { };
>> foo!string(f, 1);
>
> Doesn't work for me. Still missing the string argument.
>

Yes, same typo.

>> The problem with this is that when I want to pass no arguments,
>>
>> auto f = (int x) { };
>> foo(f, 1);
>>
>> fails. It seems that Args... requires at least one argument to match the
>> template? This may be a bug?
>
> One thing you need to fix: The `callback` template needs a template sequence parameter (i.e. `Args...`). Otherwise it takes exactly one type.

I did try that first and it didn't work. it works without ..., and I figured that it is a template parameter and can also represent a sequence? But I only tried with one argument so it worked.

I added ... but same problems.

> That doesn't make it work, though. You also need to add empty template instantiation parentheses (i.e. `foo!()`), and you need to remove `auto ref` from the `args` parameter.
>
> No idea why it doesn't work with `auto ref`. At least that part looks like a bug to me.


Yeah, so, this is typically what happens. One bug makes me change my tail for two hours ;/
August 08, 2016
On 08/08/2016 12:08 AM, Engine Machine wrote:
> On Sunday, 7 August 2016 at 20:48:29 UTC, ag0aep6g wrote:
[...]
>> Delegates don't necessarily need a GC allocation. They only need it
>> when they need a closure. Delegates of methods don't need closures.
>> And when you pass the delegate in a `scope` parameter, no closure is
>> needed, either.
>
> Well, one can't pick the case the delegate is passed. When I use a
> delegate in the nogc context it errs.

Not exactly. When you do something that requires a closure, it errors out. As I said, a delegate doesn't always require the allocation of a closure.

This works just fine, and it uses a delegate parameter:

----
@nogc void foo(void delegate(int x) @nogc f) {}

void main() @nogc
{
    foo((int x) {});

    struct S
    {
        int y;
        void f(int x) @nogc { this.y = x; }
    }
    S s;
    foo(&s.f);
}
----

But this doesn't compile, because the delegate here would need a closure:

----
@nogc void foo(void delegate(int x) @nogc f) {}

void main() @nogc
{
    int y;
    foo((int x) { y = x; });
}
----

Also note that it's main's @nogc that makes this fail, not foo's or the parameter's. Remove main's @nogc and it works.

[...]
>> You're missing an argument there. The second parameter of foo is
>> `args` which is `string` here. This call works:
>>
>>     foo!string((int x, string s) { }, "", 1);
>>
>
> Yeah, that was just a typeo obvious. That's not the reason it fails.

No. It's exactly the reason it fails. Add a string argument and it works:

----
alias callback(Args) = @nogc void function(int x, Args);
@nogc void foo(Args...)(callback!Args f, auto ref Args args, int extra = 0) {}

void main() @nogc
{
    foo!string((int x, string s) { }, "", 1);
}
----

[...]
>> One thing you need to fix: The `callback` template needs a template
>> sequence parameter (i.e. `Args...`). Otherwise it takes exactly one type.
>
> I did try that first and it didn't work. it works without ..., and I
> figured that it is a template parameter and can also represent a
> sequence?

No, without `...`, the template parameter only accepts exactly one type, not more than one, not none.
August 07, 2016
On Sunday, 7 August 2016 at 23:02:26 UTC, ag0aep6g wrote:
> Not exactly. When you do something that requires a closure, it errors out. As I said, a delegate doesn't always require the allocation of a closure.

You can also throw scope in there iff the delegate will never be stored:

@nogc void foo(scope void delegate(int x) @nogc f) {}


Then it won't allocate the closure even if it is a context that usually needs it, but if you store it then, you are liable to memory corruption.
August 08, 2016
On Sunday, 7 August 2016 at 23:02:26 UTC, ag0aep6g wrote:
> On 08/08/2016 12:08 AM, Engine Machine wrote:
>> On Sunday, 7 August 2016 at 20:48:29 UTC, ag0aep6g wrote:
> [...]
>>> Delegates don't necessarily need a GC allocation. They only need it
>>> when they need a closure. Delegates of methods don't need closures.
>>> And when you pass the delegate in a `scope` parameter, no closure is
>>> needed, either.
>>
>> Well, one can't pick the case the delegate is passed. When I use a
>> delegate in the nogc context it errs.
>
> Not exactly. When you do something that requires a closure, it errors out. As I said, a delegate doesn't always require the allocation of a closure.
>
> This works just fine, and it uses a delegate parameter:
>
> ----
> @nogc void foo(void delegate(int x) @nogc f) {}
>
> void main() @nogc
> {
>     foo((int x) {});
>
>     struct S
>     {
>         int y;
>         void f(int x) @nogc { this.y = x; }
>     }
>     S s;
>     foo(&s.f);
> }
> ----
>
> But this doesn't compile, because the delegate here would need a closure:
>
> ----
> @nogc void foo(void delegate(int x) @nogc f) {}
>
> void main() @nogc
> {
>     int y;
>     foo((int x) { y = x; });
> }
> ----
>
> Also note that it's main's @nogc that makes this fail, not foo's or the parameter's. Remove main's @nogc and it works.
>
> [...]
>>> You're missing an argument there. The second parameter of foo is
>>> `args` which is `string` here. This call works:
>>>
>>>     foo!string((int x, string s) { }, "", 1);
>>>
>>
>> Yeah, that was just a typeo obvious. That's not the reason it fails.
>
> No. It's exactly the reason it fails. Add a string argument and it works:
>
> ----
> alias callback(Args) = @nogc void function(int x, Args);
> @nogc void foo(Args...)(callback!Args f, auto ref Args args, int extra = 0) {}
>
> void main() @nogc
> {
>     foo!string((int x, string s) { }, "", 1);
> }
> ----
>
> [...]
>>> One thing you need to fix: The `callback` template needs a template
>>> sequence parameter (i.e. `Args...`). Otherwise it takes exactly one type.
>>
>> I did try that first and it didn't work. it works without ..., and I
>> figured that it is a template parameter and can also represent a
>> sequence?
>
> No, without `...`, the template parameter only accepts exactly one type, not more than one, not none.


So, what about passing in the lambda verses the temp variable?

I tried the ... same problem as I said.

f!()(...) doesn't work.

Everything works find as long as I pass more than one variable.


My code is as follows and I cannot get 0 parameters working nor passing the function in directly. These were my original questions to begin with and haven't been answered. Here is the code I am using.

alias callback(Args...) = @nogc void function(string, int, Args);
@nogc public void foo(Args...)(callback!Args c, Args args, int x) { }

foo((string s, int i) { }, 1);

does not work

nor does

foo!()((string s, int i) { }, 1);


Ultimately it would also be nice if the template parameters were deduced automatically instead of having to specify them. The compiler should be able to figure out the types supplied parameters.

e.g.,

foo((string s, int i, T x) { }, someT, 1);

instead of

foo!(T)((string s, int i, T x) { }, someT, 1);

should it not?




	
August 08, 2016
On 08/08/2016 02:42 AM, Engine Machine wrote:
> So, what about passing in the lambda verses the temp variable?

Nothing. I didn't see a case where it worked one way but not the other. If you have code where adding a variable makes things work, please post a complete test case.

[...]
> My code is as follows and I cannot get 0 parameters working nor passing
> the function in directly. These were my original questions to begin with
> and haven't been answered. Here is the code I am using.
>
> alias callback(Args...) = @nogc void function(string, int, Args);
> @nogc public void foo(Args...)(callback!Args c, Args args, int x) { }
>
> foo((string s, int i) { }, 1);
>
> does not work
>
> nor does
>
> foo!()((string s, int i) { }, 1);

The second one does work. https://dpaste.dzfl.pl/212b467c62a4

> Ultimately it would also be nice if the template parameters were deduced
> automatically instead of having to specify them. The compiler should be
> able to figure out the types supplied parameters.
>
> e.g.,
>
> foo((string s, int i, T x) { }, someT, 1);
>
> instead of
>
> foo!(T)((string s, int i, T x) { }, someT, 1);
>
> should it not?

I think the `callback` template is the problem here.

Template instantiation is a one way street. You generally can't answer the question "With what arguments would template Foo need to be instantiated to get result Bar?". For starters, multiple different arguments may map to the same result, making the reverse ambiguous.

It works when you skip the `callback` template and put the function/delegate type directly into the signature:

----
@nogc public void foo(Args...)(void function(string, int, Args) @nogc c,
    Args args, int x)
{}

void main() @nogc
{
    foo((string s, int i) { }, 1);

    alias T = float;
    T someT;
    foo((string s, int i, T x) { }, someT, 1);
}
----