digitalmars.D.learn - delegates, functions, and literals confusion
- CJS (33/33) Jul 03 2013 Confusion over delegates seems to be a somewhat common topic,
- Mike Parker (55/78) Jul 04 2013 First, to clarify some terminology.
- CJS (5/5) Jul 04 2013 Thanks for the detailed answer!
- Mike Parker (22/27) Jul 04 2013 That is correct. Unless the inner function is static. I had
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
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
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
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