digitalmars.D - question about foreach, opApply, and delegates
- Jerry Quinn (20/20) Jun 08 2009 Hi, all. I find myself a little confused about how foreach, opApply, an...
- grauzone (26/31) Jun 08 2009 The foreach body is like a nested function. Your example is (probably,
-
BCS
(6/36)
Jun 08 2009
In one way of looking at it every stack frame is a virtual struct
so... - Jarrett Billingsley (16/36) Jun 08 2009 being used). =A0So if we use a simple example below, what exactly is th...
- downs (4/8) Jun 08 2009 If the docs say that, they're wrong.
- Jerry Quinn (4/16) Jun 08 2009 Yes, that helps. All 3 replies are basically the same, and the docs are...
Hi, all. I find myself a little confused about how foreach, opApply, and delegates interact according to the docs. Foreach on an aggregate will use the opApply call (assuming ranges aren't being used). So if we use a simple example below, what exactly is the delegate that is passed to opApply? The docs say a delegate is a pairing of an object reference and a function, where the object is passed as the 'this' parameter to the function. But that doesn't seem to be the case here. Is a virtual object with a function encompassing the body of the foreach being silently created and passed in? Thanks, Jerry class C { uint[] a; int opApply(int delegate(ref uint) dg) { int result = 0; for (size_t i=0; i < a.length; i++) { result = dg(a[i]); if (result) break; } return result; } } void foo() { C c = new C; foreach (uint v; c) { writefln(v); } }
Jun 08 2009
Jerry Quinn wrote:Hi, all. I find myself a little confused about how foreach, opApply, and delegates interact according to the docs. Foreach on an aggregate will use the opApply call (assuming ranges aren't being used). So if we use a simple example below, what exactly is the delegate that is passed to opApply? The docs say a delegate is a pairing of an object reference and a function, where the object is passed as the 'this' parameter to the function. But that doesn't seem to be the case here. Is a virtual object with a function encompassing the body of the foreach being silently created and passed in?The foreach body is like a nested function. Your example is (probably, maybe this is wrong/oversimplified) compiled to something like this: void foo() { C c = new C; int bla; void something(uint v) { bla = 123; writefln(v); } c.opApply(&something); } Note that &something is a delegate. A delegate is a pair of a context pointer and a function pointer. As you said, the context pointer can be an object reference. But in this case, it's a raw pointer into the stack. That's why you can use variables declared in the containing function (foo). I added a variable 'bla' to demonstrate this. When the nested function accesses bla, it reads the delegate's context pointer to access the stack frame of the containing function. You could say the foreach statement is a giant pile of syntactic sugar. No magic involved. When the break or continue statements are used, things get more trickier. That's why the delegate passed to opApply returns an int. I think. Also, this should go into the d.D.learn newsgroup. (Although I think it's perfectly fine to post this here, because the "gurus" don't post and probably don't even read d.D.learn.)
Jun 08 2009
Hello grauzone,Jerry Quinn wrote:In one way of looking at it every stack frame is a virtual struct <g> so yes, sort of.Hi, all. I find myself a little confused about how foreach, opApply, and delegates interact according to the docs. Foreach on an aggregate will use the opApply call (assuming ranges aren't being used). So if we use a simple example below, what exactly is the delegate that is passed to opApply? The docs say a delegate is a pairing of an object reference and a function, where the object is passed as the 'this' parameter to the function. But that doesn't seem to be the case here. Is a virtual object with a function encompassing the body of the foreach being silently created and passed in?The foreach body is like a nested function. Your example is (probably, maybe this is wrong/oversimplified) compiled to something like this: void foo() { C c = new C; int bla; void something(uint v) { bla = 123; writefln(v); } c.opApply(&something); }IIRC if you compile with the -v flag you can even see DMD running code generation for the foreach bodies.(Although I think it's perfectly fine to post this here, because the "gurus" don't post and probably don't even read d.D.learn.)I may not count as a "guru" but I'm "not a beginner" and do read it.
Jun 08 2009
On Mon, Jun 8, 2009 at 8:55 AM, Jerry Quinn<jlquinn optonline.net> wrote:Hi, all. =A0I find myself a little confused about how foreach, opApply, a=nd delegates interact according to the docs.Foreach on an aggregate will use the opApply call (assuming ranges aren't=being used). =A0So if we use a simple example below, what exactly is the d= elegate that is passed to opApply? =A0The docs say a delegate is a pairing = of an object reference and a function, where the object is passed as the 't= his' parameter to the function. =A0But that doesn't seem to be the case her= e.Is a virtual object with a function encompassing the body of the foreach =being silently created and passed in? Sort of. The delegate passed into the opApply in this case is actually the body of the loop. The compiler transforms the foreach loop body into a nested function. Nested functions are delegates, but their context pointer is a pointer to their enclosing function's stack frame rather than to an object. So no object is created, but it still transparently works like a delegate. This is also why in D1 returning nested functions results in undefined behavior: the pointer to the enclosing function's stack frame is no longer valid.Thanks, Jerry class C { =A0uint[] a; =A0int opApply(int delegate(ref uint) dg) { =A0 =A0int result =3D 0; =A0 =A0for (size_t i=3D0; i < a.length; i++) { =A0 =A0 =A0result =3D dg(a[i]); =A0 =A0 =A0if (result) break; =A0 =A0} =A0 =A0return result; =A0} } void foo() { =A0C c =3D new C; =A0foreach (uint v; c) { writefln(v); } }
Jun 08 2009
Jerry Quinn wrote:Hi, all. I find myself a little confused about how foreach, opApply, and delegates interact according to the docs. Foreach on an aggregate will use the opApply call (assuming ranges aren't being used). So if we use a simple example below, what exactly is the delegate that is passed to opApply?The docs say a delegate is a pairing of an object reference and a function, where the object is passed as the 'this' parameter to the function. But that doesn't seem to be the case here.If the docs say that, they're wrong. Generally speaking, a delegate is a pairing of a function pointer and a context, where the context can be a struct pointer, a class reference *or a stackframe*, as is the case with opApply. Hope that clears things up.
Jun 08 2009
downs Wrote:Jerry Quinn wrote:Yes, that helps. All 3 replies are basically the same, and the docs are clearly insufficient to describe what's actually happening in a delegate. I'll file a bug. Thanks!Hi, all. I find myself a little confused about how foreach, opApply, and delegates interact according to the docs. Foreach on an aggregate will use the opApply call (assuming ranges aren't being used). So if we use a simple example below, what exactly is the delegate that is passed to opApply?The docs say a delegate is a pairing of an object reference and a function, where the object is passed as the 'this' parameter to the function. But that doesn't seem to be the case here.If the docs say that, they're wrong. Generally speaking, a delegate is a pairing of a function pointer and a context, where the context can be a struct pointer, a class reference *or a stackframe*, as is the case with opApply. Hope that clears things up.
Jun 08 2009