Passing a function as an argument to another function : what is this sorcery ?
Gary Willoughby via Digitalmars-d-learn
digitalmars-d-learn at puremagic.com
Sun May 25 03:18:35 PDT 2014
On Sunday, 25 May 2014 at 09:37:46 UTC, Derix wrote:
> Hello everyone,
>
> So I'm "Getting Started With Gtkd" [1] and the tuto includes
> this
> piece of code :
>
> ...
>
> DrawingArea da = new DrawingArea(590, 200);
> da.addOnDraw(&onDraw);
> layout.put(da, 5, 30);
>
> add(layout); // Add the layout to our main window
> showAll();
> }
>
> bool onDraw(Context c, Widget w)
> {
> //draw things
> return true;
> }
> }
>
> and I'm a bit puzzled by the line
> da.addOnDraw(&onDraw);
>
> I'm pretty sure I've met this construct before along with
> relevant explainations, but I find myself a bit forgetful and in
> need of a refresher. Hence questions :
>
> 1) What is the generic name for this kind construct ?
>
> 2) Any hint to some reading about the supporting theory or
> rationale ?
It's basically passing an existing function via a pointer. Notice
the ampersand (&)? That gets the underlying memory location (i.e.
pointer). Normal procedural type functions can be passed like
this as can methods of classes (if you have access to them). When
passing a class method like this it can be referred to as passing
a delegate. Delegates internally store another pointer to it's
surrounding context.
For example, a function can be passed like this:
void foo()
{
// Do something.
}
bar.addFoo(&foo);
Then in the addFoo method foo can be called as if it was a normal
function. You can also write the above like this:
bar.addFoo(function(){
// Do something.
});
As function literals are passed via a pointer by default so no
ampersand needed.
The problem is that normal functions can not refer to anything
outside of their scope. For this you need a delegate which
contains a context pointer (which is a reference to it's
surroundings). Here is an example if using a class method.
class Foo
{
public void bar()
{
// Do something.
}
public void baz()
{
// Refers to bar outside of this method's scope.
this.bar();
}
}
auto foo = new Foo();
// baz can refer to the instance of Foo (it's context)
// so it can call bar.
qux.addBaz(&foo.baz);
You can also use the literal notation too:
qux.addBaz(delegate(){
// I can now use things outside this scope.
});
Like function literals, delegates are also passed by their
pointer by default.
>
> 3) When the onDraw function is actually called, whence does it
> takes its arguments from ? What is that Context ? Does it float
> around as some sort of implicit global object ? When was it
> instanciated ?
When passing functions or methods like this they need to be typed
just like anything else you would pass as an argument. Like this:
alias void delegate(string) MyCallback;
Here we define the signature of a delegate using an alias. Once a
method is defined taking this alias as a type then it will only
accept a delegate with this signature.
class Foo
{
// Only accept 'void delegate(string)'
void bar(MyCallback baz)
{
// call conforming to the MyCallback signature.
baz("hello world"); <--
}
}
auto foo = new Foo();
// Passed delegate conforms to the MyCallback signature.
foo.bar(delegate(string x){
writeln(x);
});
Functions are typed the same way:
alias void function(string) MyCallback;
> 4) Is &onDraw a predefined event belonging to the class
> DrawingArea ? Is the name of the onDraw function thus
> constrained
> ?
onDraw will just be a method that accepts a delegate (or standard
function).
> 5) What is the chain of events leading to the "onDraw" event
> occuring ? I'd guess it's the 'showAll()' line, but by the way
> where does this 'showAll()' come from ? In the documentation
I've no idea you'll have to read the source to find that out.
> I don't see it as a method belonging to the class DrawingArea.
> Is
> it inherited from a superclass ?
Probably.
More information about the Digitalmars-d-learn
mailing list