www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Generalizing over function pointers and delegates

reply Bastiaan Veelo <Bastiaan Veelo.net> writes:
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
Feb 15 2019
next sibling parent reply Alex <sascha.orlov gmail.com> writes:
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
Feb 15 2019
parent Bastiaan Veelo <Bastiaan Veelo.net> writes:
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); } ​```
Feb 15 2019
prev sibling parent reply ag0aep6g <anonymous example.com> writes:
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
Feb 15 2019
next sibling parent Bastiaan Veelo <Bastiaan Veelo.net> writes:
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.
Feb 15 2019
prev sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
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.
Feb 15 2019
parent Bastiaan Veelo <Bastiaan Veelo.net> writes:
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!
Feb 15 2019