View mode: basic / threaded / horizontal-split · Log in · Help
September 21, 2011
Heap fucntion calls
D has a wonderfull feature named delegate. Delegate can acess local 
data, thus would be dangerous if thoses data were on the stack. For what 
I understand, when a delegate can access the local data of a function, 
those data are set on the heap instead of the stack, resulting on a 
slower function call, but on a safe delegate behaviour.

I'm wondering what's going on behind the hood when such a function is 
called. are the parameter passed to the function on the stack and the 
copied on the heap ? In such a situation, data are copied two times. 
Will a postblit constructor be called two times ? Or is the function 
taggued as « heap function » and then only the pointer is passed in the 
function call ?

Secondly, how does thing like scope(exit) are handled in such a case ? 
When the constext is collected by the GC ? When the function ends it's 
execution ? The try {} finally {} analogy suggest the second one, but 
this is definitively not an exit of the scope, the scope being still 
accsible throw the delegate.

Those are exemple but more generaly, my question isn't about thoses 
exemples. It is about what really is going on. Let's say, what would be 
the C translation of such a function call or somethung similar.

Thank by adavnce,

deadalnix
September 21, 2011
Re: Heap fucntion calls
On Wed, 21 Sep 2011 18:32:49 +0200, deadalnix <deadalnix@gmail.com> wrote:

> D has a wonderfull feature named delegate. Delegate can acess local  
> data, thus would be dangerous if thoses data were on the stack. For what  
> I understand, when a delegate can access the local data of a function,  
> those data are set on the heap instead of the stack, resulting on a  
> slower function call, but on a safe delegate behaviour.
>
> I'm wondering what's going on behind the hood when such a function is  
> called. are the parameter passed to the function on the stack and the  
> copied on the heap ? In such a situation, data are copied two times.  
> Will a postblit constructor be called two times ? Or is the function  
> taggued as « heap function » and then only the pointer is passed in the  
> function call ?

It's the latter. A delegate is simply a function pointer/context pointer
pair, and the exact same thing is used for pointers to member functions
as for lexical closures.


> Secondly, how does thing like scope(exit) are handled in such a case ?  
> When the constext is collected by the GC ? When the function ends it's  
> execution ? The try {} finally {} analogy suggest the second one, but  
> this is definitively not an exit of the scope, the scope being still  
> accsible throw the delegate.

scope(exit) foo();
// stuff

is simply rewritten as

try {
    // stuff
}
finally {
    foo();
}

Hence, again the latter is the case. In this case:

string delegate() foo() {
    string s = "initialized";
    scope( exit ) s = "destroyed";
    auto ret = (){return s;}
    return ret;
}

void bar() {
    assert(foo()() == "destroyed");
}

The assert passes.


> Those are exemple but more generaly, my question isn't about thoses  
> exemples. It is about what really is going on. Let's say, what would be  
> the C translation of such a function call or somethung similar.

void foo() {
    int x = 5;
    auto dg = () {x = 4;}
    dg();
}

is roughly equivalent to:

typedef struct foo_dg_1_delegate {
    void (*funcptr)(struct foo_dg_1_context*);
    void* ptr;
};

typedef struct foo_dg_1_context {
    int x;
};

void foo_dg_1(struct foo_dg_1_context* ctx) {
    ctx->x = 4;
}

void foo(void) {
    struct foo_dg_1_delegate dg;
    struct foo_dg_1_context* ctx = (struct  
foo_dg_1_context*)malloc(sizeof(struct foo_dg_1_context));
    dg.funcptr = &foo_dg_1;
    dg.ptr = ctx;
    ctx->x = 5;
    dg.funcptr(dg.ptr);
}

-- 
  Simen
September 21, 2011
Re: Heap fucntion calls
Great answer ! Thank you very much, it answered almost everything !

But what about, in the exemple you gave me (which is great by the way) 
if foo as parameters ? Those parameters are passed on the stack by copy 
to the function, and then, copied to the heap (resulting in two copies) ?

Le 21/09/2011 19:56, Simen Kjaeraas a écrit :
> void foo() {
> int x = 5;
> auto dg = () {x = 4;}
> dg();
> }
>
> is roughly equivalent to:
>
> typedef struct foo_dg_1_delegate {
> void (*funcptr)(struct foo_dg_1_context*);
> void* ptr;
> };
>
> typedef struct foo_dg_1_context {
> int x;
> };
>
> void foo_dg_1(struct foo_dg_1_context* ctx) {
> ctx->x = 4;
> }
>
> void foo(void) {
> struct foo_dg_1_delegate dg;
> struct foo_dg_1_context* ctx = (struct
> foo_dg_1_context*)malloc(sizeof(struct foo_dg_1_context));
> dg.funcptr = &foo_dg_1;
> dg.ptr = ctx;
> ctx->x = 5;
> dg.funcptr(dg.ptr);
> }
>
September 22, 2011
Re: Heap fucntion calls
On Thu, 22 Sep 2011 00:43:09 +0200, deadalnix <deadalnix@gmail.com> wrote:

> Great answer ! Thank you very much, it answered almost everything !
>
> But what about, in the exemple you gave me (which is great by the way)  
> if foo as parameters ? Those parameters are passed on the stack by copy  
> to the function, and then, copied to the heap (resulting in two copies) ?

Oui. In that case:

void foo(int n, ref int i) { // Adding in a ref, for good measure.
    n = 2;
    int x = 5;
    auto dg = () {x = 4; n = 3; i = 2;};
    dg();
}

becomes:

typedef struct foo_dg_1_delegate {
    void (*funcptr)(struct foo_dg_1_context*);
    void* ptr;
};

typedef struct foo_dg_1_context {
    int x;
    int n;
    int* i;
};

void foo_dg_1(struct foo_dg_1_context* ctx) {
    ctx->x = 4;
    ctx->n = 3;
    *ctx->i = 2;
}

void foo(int n, int* i) {
    struct foo_dg_1_delegate dg;
    struct foo_dg_1_context* ctx = (struct  
foo_dg_1_context*)malloc(sizeof(struct foo_dg_1_context));
    dg.funcptr = &foo_dg_1;
    dg.ptr = ctx;
    ctx->x = 5;
    ctx->n = n; // Unnecessary initialization, but conceptually happens.
    ctx->i = i;

    ctx->n = 2;
    dg.funcptr(dg.ptr);
}

-- 
  Simen
Top | Discussion index | About this forum | D home