Jump to page: 1 26  
Page
Thread overview
Full closures for D
Nov 02, 2007
Walter Bright
Nov 02, 2007
Denton Cockburn
Nov 05, 2007
KlausO
Nov 05, 2007
Bruce Adams
Nov 05, 2007
0ffh
Nov 06, 2007
David B. Held
Nov 10, 2007
Lars Noschinski
Nov 05, 2007
0ffh
Nov 06, 2007
Denton Cockburn
Nov 02, 2007
Nathan Reed
Nov 02, 2007
Xinok
Nov 03, 2007
Extrawurst
Nov 02, 2007
0ffh
Nov 03, 2007
Walter Bright
Nov 03, 2007
Walter Bright
Nov 03, 2007
Craig Black
Nov 03, 2007
Reiner Pope
Nov 03, 2007
Reiner Pope
Nov 03, 2007
David Medlock
Nov 03, 2007
David Medlock
Nov 06, 2007
Martin d Anjou
Nov 06, 2007
Bruce Adams
Nov 03, 2007
Steve Teale
Nov 03, 2007
bearophile
Nov 07, 2007
Witold Baryluk
Nov 07, 2007
outersky
Nov 07, 2007
Russell Lewis
Nov 07, 2007
outersky
Nov 07, 2007
Witold Baryluk
Nov 07, 2007
Bill Baxter
Nov 07, 2007
Bill Baxter
Nov 07, 2007
David B. Held
Nov 07, 2007
Nathan Reed
Nov 07, 2007
David B. Held
Nov 07, 2007
Nathan Reed
Nov 07, 2007
Simas
Nov 07, 2007
Bill Baxter
Nov 08, 2007
David B. Held
Nov 07, 2007
Nathan Reed
Nov 07, 2007
Bill Baxter
Nov 07, 2007
Nathan Reed
Nov 07, 2007
Bill Baxter
Nov 08, 2007
David B. Held
Nov 07, 2007
Janice Caron
Nov 07, 2007
David B. Held
Nov 07, 2007
Robert Fraser
Nov 11, 2007
Frank Benoit
November 02, 2007
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
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
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
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
"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
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
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
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
"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
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.
« First   ‹ Prev
1 2 3 4 5 6