Thread overview | |||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
November 02, 2007 Full closures for D | ||||
---|---|---|---|---|
| ||||
D 2.007 brings full closures to D. I was tired of D being denigrated for not having "real" closures. Here are some test cases for it, so you can see what it does: ------------------------------------------- struct S { int a,b,c,d; } alias int delegate() dg_t; alias int delegate(int) dg1_t; void fill() { int[100] x; } /************************************/ dg_t foo() { int x = 7; int bar() { return x + 3; } return &bar; } void test1() { dg_t dg = foo(); fill(); printf("bar = %d\n", dg()); assert(dg() == 10); } /************************************/ dg_t foo2() { dg_t abc() { int x = 7; int bar() { return x + 3; } return &bar; } return abc(); } void test2() { dg_t dg = foo2(); fill(); printf("bar = %d\n", dg()); assert(dg() == 10); } /************************************/ dg_t foo3() { dg_t abc(int x) { int bar() { return x + 3; } return &bar; } return abc(7); } void test3() { dg_t dg = foo3(); fill(); printf("bar = %d\n", dg()); assert(dg() == 10); } /************************************/ dg_t foo4() { S s; s = S(4,5,6,7); dg_t abc(S t) { int bar() { return t.d + 3; } return &bar; } return abc(s); } void test4() { dg_t dg = foo4(); fill(); printf("bar = %d\n", dg()); assert(dg() == 10); } /************************************/ void test5() { int x = 7; dg_t abc(ref int y) { int bar() { y += 4; return y + 3; } return &bar; } dg_t dg = abc(x); fill(); assert(x == 7); auto i = dg(); assert(x == 11); assert(i == 14); x = 8; i = dg(); assert(x == 12); assert(i == 15); } /************************************/ void test6() { int x = 7; dg_t abc(out int y) { int bar() { y += 4; return y + 3; } return &bar; } dg_t dg = abc(x); fill(); assert(x == 0); auto i = dg(); assert(x == 4); assert(i == 7); x = 8; i = dg(); assert(x == 12); assert(i == 15); } /************************************/ void test7() { int[3] a = [10,11,12]; dg_t abc(int[3] y) { int bar() { y[2] += 4; return y[2] + 3; } return &bar; } dg_t dg = abc(a); fill(); assert(a[2] == 12); auto i = dg(); assert(a[2] == 16); assert(i == 19); } /************************************/ void test8() { S s = S(7,8,9,10); dg_t abc(ref S t) { int bar() { t.d += 4; return t.c + 3; } return &bar; } dg_t dg = abc(s); fill(); assert(s.d == 10); auto i = dg(); assert(s.d == 14); assert(i == 12); } /************************************/ S foo9(out dg_t dg) { S s1 = S(7,8,9,10); dg_t abc() { int bar() { s1.d += 4; return s1.c + 3; } return &bar; } dg = abc(); return s1; } void test9() { dg_t dg; S s = foo9(dg); fill(); assert(s.a == 7); assert(s.b == 8); assert(s.c == 9); assert(s.d == 10); auto i = dg(); assert(s.d == 10); assert(i == 12); } /************************************/ dg_t foo10() { dg_t abc() { int x = 7; int bar() { int def() { return x + 3; } return def(); } return &bar; } return abc(); } void test10() { dg_t dg = foo10(); fill(); printf("bar = %d\n", dg()); assert(dg() == 10); } /************************************/ dg_t foo11() { int x = 7; class T { int bar() { return x + 3; } } T t = new T; return &t.bar; } void test11() { dg_t dg = foo11(); fill(); printf("bar = %d\n", dg()); assert(dg() == 10); } /************************************/ dg_t foo12() { int x = 7; class T { int bar() { return x + 3; } int xyz() { return bar(); } } T t = new T; return &t.xyz; } void test12() { dg_t dg = foo12(); fill(); printf("bar = %d\n", dg()); assert(dg() == 10); } /************************************/ dg_t foo13() { int x = 7; class T { int xyz() { int bar() { return x + 3; } return bar(); } } T t = new T; return &t.xyz; } void test13() { dg_t dg = foo13(); fill(); printf("bar = %d\n", dg()); assert(dg() == 10); } /************************************/ dg_t foo14() { class T { int xyz() { int x = 7; int bar() { return x + 3; } return bar(); } } T t = new T; return &t.xyz; } void test14() { dg_t dg = foo14(); fill(); printf("bar = %d\n", dg()); assert(dg() == 10); } /************************************/ dg_t foo15() { class T { int x = 7; int xyz() { int bar() { return x + 3; } return bar(); } } T t = new T; return &t.xyz; } void test15() { dg_t dg = foo15(); fill(); printf("bar = %d\n", dg()); assert(dg() == 10); } /************************************/ dg_t foo16() { int a = 5; class T { int x = 7; int xyz() { int y = 8; int bar() { return a + x + y + 3; } return bar(); } } T t = new T; return &t.xyz; } void test16() { dg_t dg = foo16(); fill(); printf("bar = %d\n", dg()); assert(dg() == 23); } /************************************/ dg_t foo17() { int a = 5; class T { int x = 7; dg_t xyz() { int y = 8; int bar() { return a + x + y + 3; } return &bar; } } T t = new T; return t.xyz(); } void test17() { dg_t dg = foo17(); fill(); printf("bar = %d\n", dg()); assert(dg() == 23); } /************************************/ dg_t dg18; void bar18() { int a = 7; int foo() { return a + 3; } dg18 = &foo; int i = dg18(); assert(i == 10); } void test18() { bar18(); fill(); int i = dg18(); assert(i == 10); } /************************************/ void abc19(void delegate() dg) { dg(); dg(); dg(); } struct S19 { static S19 call(int v) { S19 result; result.v = v; void nest() { result.v += 1; } abc19(&nest); return result; } int a; int v; int x,y,z; } int foo19() { auto s = S19.call(5); return s.v; } void test19() { int i = foo19(); printf("%d\n", i); assert(i == 8); } /************************************/ int main() { test1(); test2(); test3(); test4(); test5(); test6(); test7(); test8(); test9(); test10(); test11(); test12(); test13(); test14(); test15(); test16(); test17(); test18(); test19(); printf("Success\n"); return 0; } |
November 02, 2007 Re: Full closures for D | ||||
---|---|---|---|---|
| ||||
Posted in reply to Walter Bright | On Fri, 02 Nov 2007 14:03:59 -0700, Walter Bright wrote:
> D 2.007 brings full closures to D. I was tired of D being denigrated for not having "real" closures.
>
Nicely Done. Lisp is looking less special everyday.
|
November 02, 2007 Re: Full closures for D | ||||
---|---|---|---|---|
| ||||
Posted in reply to Walter Bright | Walter Bright wrote:
> D 2.007 brings full closures to D. I was tired of D being denigrated for not having "real" closures.
Very cool. I have to admit I didn't think this would make it into the language. :)
I'm curious about how they work. I'd guess you dup the stack frame onto the heap when a function returns a delegate, and update the delegate's context pointer?
Thanks,
Nathan Reed
|
November 02, 2007 Re: Full closures for D | ||||
---|---|---|---|---|
| ||||
Posted in reply to Nathan Reed | It seems that variables that are used by a nested function are allocated on the heap rather than the stack. This was my test code:
void delegate() foo(){
int v = 60;
int c = 35;
writefln(&c);
writefln(&v);
return {writefln(&v);};
}
void main(){
void delegate() one = foo();
one();
}
Prints:
12FF18
8B2FF4
8B2FF4
The address of 'v' doesn't change. What you do notice is the great difference of the memory addresses between int v and int c, which suggests that 'v' is allocated on the heap rather than the stack.
Nathan Reed wrote:
> Walter Bright wrote:
>> D 2.007 brings full closures to D. I was tired of D being denigrated for not having "real" closures.
>
> Very cool. I have to admit I didn't think this would make it into the language. :)
>
> I'm curious about how they work. I'd guess you dup the stack frame onto the heap when a function returns a delegate, and update the delegate's context pointer?
>
> Thanks,
> Nathan Reed
|
November 02, 2007 Re: Full closures for D | ||||
---|---|---|---|---|
| ||||
Posted in reply to Xinok | "Xinok" <xnknet@gmail.com> wrote in message news:fgg9q8$jlr$1@digitalmars.com... > It seems that variables that are used by a nested function are allocated on the heap rather than the stack. This was my test code: > > void delegate() foo(){ > int v = 60; > int c = 35; > writefln(&c); > writefln(&v); > return {writefln(&v);}; > } > > void main(){ > void delegate() one = foo(); > one(); > } > > Prints: > 12FF18 > 8B2FF4 > 8B2FF4 > > The address of 'v' doesn't change. What you do notice is the great difference of the memory addresses between int v and int c, which suggests that 'v' is allocated on the heap rather than the stack. > Hm. Don't have a D2 compiler with me -- could you run the following and tell me what it prints? void main() { int v, c; void foo() { writefln(&c); } writefln(&v); writefln(&c); } I'm wondering if the compiler is smart enough not to allocate variables on the heap if it doesn't have to. (I'm not returning foo.) |
November 02, 2007 Re: Full closures for D | ||||
---|---|---|---|---|
| ||||
Posted in reply to Walter Bright | Walter Bright wrote:
> D 2.007 brings full closures to D. I was tired of D being denigrated for not having "real" closures.
Sometimes I think you must really enjoy surprising everyone.... =)
Regards, Frank
|
November 03, 2007 Re: Full closures for D | ||||
---|---|---|---|---|
| ||||
Posted in reply to Jarrett Billingsley Attachments:
| Jarrett Billingsley schrieb:
> "Xinok" <xnknet@gmail.com> wrote in message
> news:fgg9q8$jlr$1@digitalmars.com...
>
>> It seems that variables that are used by a nested function are allocated on the heap rather than the stack. This was my test code:
>>
>> void delegate() foo(){
>> int v = 60;
>> int c = 35;
>> writefln(&c);
>> writefln(&v);
>> return {writefln(&v);};
>> }
>>
>> void main(){
>> void delegate() one = foo();
>> one();
>> }
>>
>> Prints:
>> 12FF18
>> 8B2FF4
>> 8B2FF4
>>
>> The address of 'v' doesn't change. What you do notice is the great difference of the memory addresses between int v and int c, which suggests that 'v' is allocated on the heap rather than the stack.
>>
>>
>
> Hm. Don't have a D2 compiler with me -- could you run the following and tell me what it prints?
>
> void main()
> {
> int v, c;
>
> void foo()
> {
> writefln(&c);
> }
>
> writefln(&v);
> writefln(&c);
> }
>
> I'm wondering if the compiler is smart enough not to allocate variables on
> the heap if it doesn't have to. (I'm not returning foo.)
>
Prints:
13FF28
13FF2C
...whatever that means...
~Extrawurst
|
November 03, 2007 Re: Full closures for D | ||||
---|---|---|---|---|
| ||||
Posted in reply to 0ffh | 0ffh wrote:
> Walter Bright wrote:
>> D 2.007 brings full closures to D. I was tired of D being denigrated for not having "real" closures.
>
> Sometimes I think you must really enjoy surprising everyone.... =)
Yes, and just as I was about to upload this update, the digitalmars site went down. So I sat around chewing nails for a day till it came back up <g>.
BTW, the problem with the site was the ISP connection went down. The server was fine.
|
November 03, 2007 Re: Full closures for D | ||||
---|---|---|---|---|
| ||||
Posted in reply to Extrawurst | "Extrawurst" <spam@extrawurst.org> wrote in message Prints: 13FF28 13FF2C ...whatever that means... ~Extrawurst AGH your rich-text post is evul. But yay, they're both on the stack, meaning the compiler at least attempts to be smart about it :D No unnecessary heap allocations. |
November 03, 2007 Re: Full closures for D | ||||
---|---|---|---|---|
| ||||
Posted in reply to Walter Bright | How do D closures work? 1) The compiler makes a distinction between a nested function that 'escapes' the scope, and one that does not. It uses a very simple, but conservative, heuristic - did someone take the address of the function? If yes, it assumes the function escapes. 2) The compiler logs all local variables that are referenced by any nested function. 3) If any of those variables are referenced by an escaping nested function, or a nested function that encloses an escaping function, then upon function entry all the referenced variables are allocated on a heap-allocated chunk of memory rather than on the stack. Any referenced parameters are copied into that chunk. That chunk is linked into the nested context frames rather than the stack frame. 4) Variables not referenced by nested functions are still allocated on the stack. Note you can see this at work by running obj2asm on the test code I posted. It's not an optimal solution because the "does the function escape" heuristic captures too many functions. |
Copyright © 1999-2021 by the D Language Foundation