Thread overview
Generalizing over function pointers and delegates
Feb 15, 2019
Bastiaan Veelo
Feb 15, 2019
Alex
Feb 15, 2019
Bastiaan Veelo
Feb 15, 2019
ag0aep6g
Feb 15, 2019
Bastiaan Veelo
Feb 15, 2019
H. S. Teoh
Feb 15, 2019
Bastiaan Veelo
February 15, 2019
Given a function taking a delegate, for example

```
int fun(int delegate() dg) {return dg();}
```

Sometimes we need to call `fun` with a pointer to a nested function and other times with a pointer to a top level function. As function pointers do not implicitly convert to delegates, this does not work without jumping through hoops.

One option is to define an overload:

```
int fun(int function() fn)
{
    int nested_fn()
    {
        return fn();
    }
    return fun(&nested_fn);
}
```

This is clunky and rather a lot of extra lines to work around a language limitation. The limitation seems somewhat artificial, because a delegate /can/ be initialized referencing a top level function (the spec [1] limits that to delegate declarations at module scope, but this limit does not seem to apply [2]). But you cannot /assign/ it a top level function. You can however explicitly assign the `.funcptr` referencing the top level function, leaving the stack frame pointer null.

Exploiting this, it is possible to explicitly convert a function pointer into a delegate [2]:
```
Ret delegate(Args args) fun_to_dlg(Ret, Args...)(Ret function(Args args) fun)
{
    Ret delegate(Args) dlg;
    dlg.funcptr = fun;
    return dlg;
}
```

allowing
```
int top_level() {return -1;}
void main()
{
    assert(fun(fun_to_dlg(&top_level)) == -1);
}
```

This may be preferable to the overload, depending on the number of calls like this. But since this is allowed and working, why can't it be done automatically? Or, when implicit conversion is dangerous, why doesn't a cast exist to the effect of my template? Have I overlooked an easier way of doing this?

Thanks for any input!
Bastiaan.

P.S. For the record, the spec also says this: "Future directions: Function pointers and delegates may merge into a common syntax and be interchangeable with each other." I wish that was the case already.

[1] Point 9 in https://dlang.org/spec/function.html#closures
[2] https://run.dlang.io/is/x8HJaW
February 15, 2019
On Friday, 15 February 2019 at 14:20:44 UTC, Bastiaan Veelo wrote:
> Given a function taking a delegate, for example
>
> ```
> int fun(int delegate() dg) {return dg();}
> ```
>
> Sometimes we need to call `fun` with a pointer to a nested function and other times with a pointer to a top level function. As function pointers do not implicitly convert to delegates, this does not work without jumping through hoops.
>
> One option is to define an overload:
>
> ```
> int fun(int function() fn)
> {
>     int nested_fn()
>     {
>         return fn();
>     }
>     return fun(&nested_fn);
> }
> ```
>
> This is clunky and rather a lot of extra lines to work around a language limitation. The limitation seems somewhat artificial, because a delegate /can/ be initialized referencing a top level function (the spec [1] limits that to delegate declarations at module scope, but this limit does not seem to apply [2]). But you cannot /assign/ it a top level function. You can however explicitly assign the `.funcptr` referencing the top level function, leaving the stack frame pointer null.
>
> Exploiting this, it is possible to explicitly convert a function pointer into a delegate [2]:
> ```
> Ret delegate(Args args) fun_to_dlg(Ret, Args...)(Ret function(Args args) fun)
> {
>     Ret delegate(Args) dlg;
>     dlg.funcptr = fun;
>     return dlg;
> }
> ```
>
> allowing
> ```
> int top_level() {return -1;}
> void main()
> {
>     assert(fun(fun_to_dlg(&top_level)) == -1);
> }
> ```
>
> This may be preferable to the overload, depending on the number of calls like this. But since this is allowed and working, why can't it be done automatically? Or, when implicit conversion is dangerous, why doesn't a cast exist to the effect of my template? Have I overlooked an easier way of doing this?
>
> Thanks for any input!
> Bastiaan.
>
> P.S. For the record, the spec also says this: "Future directions: Function pointers and delegates may merge into a common syntax and be interchangeable with each other." I wish that was the case already.
>
> [1] Point 9 in https://dlang.org/spec/function.html#closures
> [2] https://run.dlang.io/is/x8HJaW

There is
https://dlang.org/library/std/functional/to_delegate.html

February 15, 2019
On Friday, 15 February 2019 at 14:30:45 UTC, Alex wrote:
>
> There is
> https://dlang.org/library/std/functional/to_delegate.html

Ah, there it is :-) Thanks.

A templated function also works.

```
int genfun(F)(F dg) {return dg();}
​
int top_level() {return -1;}
​
void main()
{
    int nested() {return -2;}
    assert(genfun(&top_level) == -1);
    assert(genfun(&nested) == -2);
}
​```
February 15, 2019
On 15.02.19 15:20, Bastiaan Veelo wrote:
> Exploiting this, it is possible to explicitly convert a function pointer into a delegate [2]:
> ```
> Ret delegate(Args args) fun_to_dlg(Ret, Args...)(Ret function(Args args) fun)
> {
>      Ret delegate(Args) dlg;
>      dlg.funcptr = fun;
>      return dlg;
> }
> ```
[...]
> This may be preferable to the overload, depending on the number of calls like this. But since this is allowed and working, why can't it be done automatically? Or, when implicit conversion is dangerous, why doesn't a cast exist to the effect of my template? Have I overlooked an easier way of doing this?

Your fun_to_dlg fails when the function has parameters.

As far as I see, it would be possible make the conversion would work by changing how a delegate's context is passed [1]. But I didn't pursue that idea further, and no one else picked it up either.

[1] https://forum.dlang.org/post/ofc0lj$2u4h$1@digitalmars.com
February 15, 2019
On Friday, 15 February 2019 at 16:40:39 UTC, ag0aep6g wrote:
> Your fun_to_dlg fails when the function has parameters.

Hah ok. std.functional.toDelegate() does work in its place though.

> As far as I see, it would be possible make the conversion would work by changing how a delegate's context is passed [1]. But I didn't pursue that idea further, and no one else picked it up either.
>
> [1] https://forum.dlang.org/post/ofc0lj$2u4h$1@digitalmars.com

Interesting. Thanks for the link.
February 15, 2019
On Fri, Feb 15, 2019 at 05:40:39PM +0100, ag0aep6g via Digitalmars-d-learn wrote:
> On 15.02.19 15:20, Bastiaan Veelo wrote:
> > Exploiting this, it is possible to explicitly convert a function
> > pointer into a delegate [2]:
> > ```
> > Ret delegate(Args args) fun_to_dlg(Ret, Args...)(Ret function(Args args)
> > fun)
> > {
> >      Ret delegate(Args) dlg;
> >      dlg.funcptr = fun;
> >      return dlg;
> > }
> > ```
[...]
> Your fun_to_dlg fails when the function has parameters.

Yes. Delegates are basically syntactic sugar for a function pointer with an implicit first parameter. I.e., a delegate like:

	int delegate(string) dg;

is under the hood implemented as the equivalent of:

	struct _delegate {
		int function(T* context, string) funcptr;
		T* context;
		int opCall(string s) { return funcptr(context, s); }
	}

where T is an appropriate context type, whether an aggregate (struct / class) or an anonymous struct that closes over whatever variables the delegate accesses in its containing scope.

For this reason, casting a function pointer to a delegate will not work properly, because the first arguments and number of parameters would not match.


T

-- 
Without outlines, life would be pointless.
February 15, 2019
On Friday, 15 February 2019 at 17:28:45 UTC, H. S. Teoh wrote:
> On Fri, Feb 15, 2019 at 05:40:39PM +0100, ag0aep6g via Digitalmars-d-learn wrote:
>> Your fun_to_dlg fails when the function has parameters.
>
> Yes. Delegates are basically syntactic sugar for a function pointer with an implicit first parameter. I.e., a delegate like:
>
> 	int delegate(string) dg;
>
> is under the hood implemented as the equivalent of:
>
> 	struct _delegate {
> 		int function(T* context, string) funcptr;
> 		T* context;
> 		int opCall(string s) { return funcptr(context, s); }
> 	}
>
> where T is an appropriate context type, whether an aggregate (struct / class) or an anonymous struct that closes over whatever variables the delegate accesses in its containing scope.
>
> For this reason, casting a function pointer to a delegate will not work properly, because the first arguments and number of parameters would not match.
>
>
> T

I love this forum!