www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - delegates, functions, and literals confusion

reply "CJS" <Prometheus85 hotmail.com> writes:
Confusion over delegates seems to be a somewhat common topic, 
judging from 
http://forum.dlang.org/thread/jbkahhlvevgectisdzdg forum.dlang.org 
and 
http://stackoverflow.com/questions/6431884/function-and-delegate-literals-in-d. 
I'm also somewhat confused by functions vs. function literals and 
how to pass them around.

In my case I'm trying to figure out the best way to pass 
functions to other fundtions. So I wrote the following toy code 
to see what the compiler would do. I'm trying to understand why 
the compiler emitted errors on the code below (shown w/ 
comments). Any insight/suggestions on better ways to pass 
functions around would also be appreciated.

import std.datetime;
import std.stdio;


void f(int delegate(int) h){
     writefln("h(0)=%d", h(0));
}

void g(int function (int) h){
     writefln("h(0)=%d", h(0));
}

void main(){

     int foo(int a){ return a+6;}
     auto bah = function int(int b){ return b+7;};

     f(foo); //error
     f(&foo);
     f(bah); //error
     f(&bah); // error

     g(foo); //error
     g(&foo); //error
     g(bah);
     g(&bah); //error

}
Jul 03 2013
parent reply "Mike Parker" <aldacron gmail.com> writes:
On Thursday, 4 July 2013 at 06:43:12 UTC, CJS wrote:
 In my case I'm trying to figure out the best way to pass 
 functions to other fundtions. So I wrote the following toy code 
 to see what the compiler would do. I'm trying to understand why 
 the compiler emitted errors on the code below (shown w/ 
 comments). Any insight/suggestions on better ways to pass 
 functions around would also be appreciated.
First, to clarify some terminology. This is a function that takes a delegate as a parameter.
 void f(int delegate(int) h){
     writefln("h(0)=%d", h(0));
 }
This is a function that takes a function *pointer*, not a function, as a parameter.
 void g(int function (int) h){
     writefln("h(0)=%d", h(0));
 }
This one is an inner function.
     int foo(int a){ return a+6;}
And this is a function literal which, in essence, is a function pointer.
     auto bah = function int(int b){ return b+7;};
In D, functions are not first class objects as they are in some other languages, so you can't actually pass them around. But what you can pass around is a pointer to a function. Try this. void g(int function (int) h) { writefln("h(0)=%d", h(0)); } void gg( int i ) { return i + 6; } void main() { g( &gg ); } Here, I'm passing a function pointer to g, which is exactly what it wants. In your code, g(bah) succeeds because when the compiler encounters a function literal, it creates a function somewhere and stores a pointer where you assign it. So your bah is a function pointer. Hence g(bah) succeeds. g(&bah) fails because &bah makes it a pointer to a function pointer, which is not the kind of parameter that g accepts. Delegates and function pointers are quite similar in usage, but architecturally different. A function pointer consists of one thing, a pointer to a function. A delegate has a pointer to a function *and* a reference to the stack frame from whence it came. There are three ways to create a delegate: via a delegate literal, via taking a pointer to a class method, or via taking a pointer to an inner method. Consider this: void main() { int i = 10; int foo( int a ) { i = a + 6; return i; } f( &foo ); writeln( i ); int j; } Because a delegate can reference the stackframe where it was created, you can use them to modify variables inside a function or to modify class member variables. Delegates declared inside a function can only modify variables declared before the inner function or delegate literal. So in my example above, foo can modify i, but trying to modify j will be a compiler error. So f(foo) fails because just 'foo' doesn't do anything. A function can be called like foo(), or a pointer can be taken with &foo, but just 'foo' is meaningless. f(&foo) succeeds because taking a pointer to an inner function creates a delegate. f(bah) fails because bah is a function pointer and not a delegate. f(&bah) fails because &bah is a pointer to a function pointer. You should now also understand why g(foo) and g(&foo) fail as well.
     f(foo); //error
     f(&foo);
     f(bah); //error
     f(&bah); // error

     g(foo); //error
     g(&foo); //error
     g(bah);
     g(&bah); //error

 }
Jul 04 2013
parent reply "CJS" <Prometheus85 hotmail.com> writes:
Thanks for the detailed answer!

Just to clarify: So if f is an inner function then &f will be a 
delegate even if it doesn't reference anything in the environment 
in which it was defined? (i.e. even if it could have been typed 
as a function?)
Jul 04 2013
parent "Mike Parker" <aldacron gmail.com> writes:
On Thursday, 4 July 2013 at 13:23:23 UTC, CJS wrote:
 Thanks for the detailed answer!

 Just to clarify: So if f is an inner function then &f will be a 
 delegate even if it doesn't reference anything in the 
 environment in which it was defined? (i.e. even if it could 
 have been typed as a function?)
That is correct. Unless the inner function is static. I had actually forgotten about this until I just looked it up in the docs[1]. Taking the address of a static inner function will give you a function pointer rather than a delegate. void foo() { static int f() { return 10; } int g() { return 20; } iTakeAFuncPtr( &f ); iTakeADelegate( &g ); } In which case you can modify static local variables: import std.stdio; void foo( int function( int ) fp ) { writeln( fp( 5 )); } void main() { static int k = 10; static int mult( int i ) { return i*10; } foo( &mult ); } [1] http://dlang.org/function.html
Jul 04 2013