digitalmars.D - Delegates
- David Medlock (31/31) Jul 21 2004 The following code did not produce results I expected:
- Sean Kelly (18/23) Jul 21 2004 This has been discussed a bit, but it's been a few months. I think the
- David Medlock (11/42) Jul 21 2004 Hmmm. The only performance hit would be at creation and not during use,...
- Sean Kelly (23/65) Jul 21 2004 The thread-safety issue was only with my workaround, not with a language...
- Russ Lewis (17/48) Jul 21 2004 I am a big proponent of having some functionality that enables us to
- David Medlock (25/50) Jul 22 2004 I assume you meant to write 'counter += val'.
- Russ Lewis (42/68) Jul 22 2004 Yes. and I also noticed that the argument to bar() has the wrong
- Sha Chancellor (7/57) Jul 22 2004 If delegates are allowed to point at the stack they were instatiated wit...
- Russ Lewis (17/24) Jul 22 2004 Stack delegates work just like any code that passes pointers to stack
- pragma (15/31) Jul 22 2004 Here's a few thoughts: what happens when a delegate, executing on it's o...
- Russ Lewis (40/54) Jul 22 2004 I think that perhaps I miscommunicated here. I don't think that the
- Kris (30/84) Jul 22 2004 Forgive me Russ for not quite following the reasoning behind this. Would...
- David Medlock (12/136) Jul 23 2004 This is what I meant above:
- Russ Lewis (21/36) Jul 23 2004 A class provides equivalent functionality, but, as you noted, it
- Kris (11/47) Jul 23 2004 Thanks!
- Russ Lewis (50/59) Jul 23 2004 I agree that it would be nice to have some mechanism for very low-cost
- Sean Kelly (4/6) Jul 22 2004 I agree. The only reason I suggested that was because it would allow us...
- Russ Lewis (3/11) Jul 22 2004 I apologize, but I'm clueless about how 'auto' comes in here. Can you
- Sean Kelly (12/23) Jul 22 2004 It was in an earlier post in this thread. Here's an example:
- Russ Lewis (27/37) Jul 22 2004 Ok, I see what you're saying. The syntax makes a lot of sense, except
- David Medlock (14/65) Jul 22 2004 Interesting.
- Sean Kelly (10/25) Jul 22 2004 Interesting idea. The only weird thing here is that this feature would ...
- Russ Lewis (27/48) Jul 22 2004 You've confused me again here. Assuming that the code in the delegate
- Sean Kelly (5/18) Jul 22 2004 Oh I misunderstood. When you said "hidden this pointer" I assumed you m...
- pragma (9/24) Jul 22 2004 I have a few friendly questions. :)
- Russ Lewis (16/45) Jul 22 2004 It's both. If you implement this sort of functionality with a class
- Sean Kelly (8/12) Jul 22 2004 Nothing really, but for some additional simplicity. A returned class ne...
- Blandger (10/14) Jul 23 2004 what
- Russ Lewis (2/28) Jul 26 2004
- Blandger (10/11) Jul 26 2004 Sorry I didn't understand a question.
- Russ Lewis (4/14) Jul 27 2004 I have posted 4 examples. See
- Blandger (5/8) Jul 27 2004 Thanks !!
The following code did not produce results I expected: int delegate() dg; alias int delegate() dg_t ; dg_t fn(int a ) { int inner() { int i = a; return i + 5; } dg_t result = &inner; return result; } void main() { dg_t delfunct, delfunct2; delfunct = fn( 100 ); delfunct2 = fn( 55 ); printf( "%i\n", delfunct() ); printf( "%i\n", delfunct2() ); printf( "%i\n", delfunct() ); printf( "%i\n", delfunct2() ); } After reading the spec I see this is normal behavior: "The stack variables, however, are not valid once the function declaring them has exited, in the same manner that pointers to stack variables are not valid upon exit from a function.." My question is why this limitation exists? The only difference for a delegate and a normal function pointer is one uses the stack for its local variables and a delegate would use a local block of memory(which can change). Maybe I am not seeing some other underlying technical issue. Sorry if this topic has come up before( I am a little new to D ). Thanks, David
Jul 21 2004
David Medlock wrote:"The stack variables, however, are not valid once the function declaring them has exited, in the same manner that pointers to stack variables are not valid upon exit from a function.." My question is why this limitation exists?This has been discussed a bit, but it's been a few months. I think the primary reason was performance. If 95% of the delgates used are not returned from functions in this manner then it's unnecessarily costly to always copy the stack frame. Some more advanced lexing might be possible to determine when this is necessary but I think the response was "maybe but not right now." One slightly lame fix that might work in a single-threaded environment is: dg_t fn(int a ) { static int cpy = a; int inner() { int i = cpy; return i + 5; } dg_t result = &inner; return result; } I suppose if this works you could use thread local storage to do the same thing in a multithreaded program. Sean
Jul 21 2004
Sean Kelly wrote:David Medlock wrote:Hmmm. The only performance hit would be at creation and not during use, so I would consider that a minor issue at best. And since the delegate 'carries' its local variables, its pretty thread safe isn't it? Adding a 'const int i = cpy' would perhaps cause the compiler to inline the whole thing, but as you say more advanced checking could optimize even in the absence of const. It just borders so close to true closures, just seems a shame they aren't there, especially since delegate is D-only and doesn't rely on C linkability(or does it?). Adding this in makes D much more functional and may attract people from functional language communities, imo."The stack variables, however, are not valid once the function declaring them has exited, in the same manner that pointers to stack variables are not valid upon exit from a function.." My question is why this limitation exists?This has been discussed a bit, but it's been a few months. I think the primary reason was performance. If 95% of the delgates used are not returned from functions in this manner then it's unnecessarily costly to always copy the stack frame. Some more advanced lexing might be possible to determine when this is necessary but I think the response was "maybe but not right now." One slightly lame fix that might work in a single-threaded environment is: dg_t fn(int a ) { static int cpy = a; int inner() { int i = cpy; return i + 5; } dg_t result = &inner; return result; } I suppose if this works you could use thread local storage to do the same thing in a multithreaded program. Sean
Jul 21 2004
In article <cdm2ml$2qfb$1 digitaldaemon.com>, David Medlock says...Sean Kelly wrote:The thread-safety issue was only with my workaround, not with a language-based stack copy. Assume this bit of code: void func() { char buf1[65536*64]; void deleg() { ... } for( ... ) { deleg(); } } The delegate is never used outside the function but the stack would have to be copied every time func() is called (assuming no advanced lexing) to make sure that deleg() always has correct data in its copy of buf. And because buf is pretty large there's a risk of out of memory conditions plus the cost of allocating the memory and copying the data. Things get a bit more complicated when you consider class method functions, as a delegate should have access to not only its own stack but to class member data as well, though I think the already existing hidden this pointer may suffice in that case.David Medlock wrote:Hmmm. The only performance hit would be at creation and not during use, so I would consider that a minor issue at best. And since the delegate 'carries' its local variables, its pretty thread safe isn't it?"The stack variables, however, are not valid once the function declaring them has exited, in the same manner that pointers to stack variables are not valid upon exit from a function.." My question is why this limitation exists?This has been discussed a bit, but it's been a few months. I think the primary reason was performance. If 95% of the delgates used are not returned from functions in this manner then it's unnecessarily costly to always copy the stack frame. Some more advanced lexing might be possible to determine when this is necessary but I think the response was "maybe but not right now." One slightly lame fix that might work in a single-threaded environment is: dg_t fn(int a ) { static int cpy = a; int inner() { int i = cpy; return i + 5; } dg_t result = &inner; return result; } I suppose if this works you could use thread local storage to do the same thing in a multithreaded program. SeanAdding a 'const int i = cpy' would perhaps cause the compiler to inline the whole thing, but as you say more advanced checking could optimize even in the absence of const.True enough. Though I'm not sure a const would work--do const values have static linkage?It just borders so close to true closures, just seems a shame they aren't there, especially since delegate is D-only and doesn't rely on C linkability(or does it?). Adding this in makes D much more functional and may attract people from functional language communities, imo.I agree. I think if this were to be done the best thing would be to add a new keyword that signals to the compiler that the stack must be copied. Perhaps use "auto" in the function declaration to signal that it is a truly local functions, while the new default behavior is to copy the stack? Sean
Jul 21 2004
I am a big proponent of having some functionality that enables us to copy stack delegates. We don't want copying to be the default, however. Consider this code: int foo() { int counter = 0; bar(void delegate(int val) { counter += arg; }); return counter; } void bar(void delegate() dg) { foreach(whatever) dg(something); } You see, in this code, you want to give the delegate the capability to modify the real values on the stack. You don't want it to be using a duplicate. So what I've advocated is that we have the option to copy or not. Sean Kelly wrote:David Medlock wrote:"The stack variables, however, are not valid once the function declaring them has exited, in the same manner that pointers to stack variables are not valid upon exit from a function.." My question is why this limitation exists?This has been discussed a bit, but it's been a few months. I think the primary reason was performance. If 95% of the delgates used are not returned from functions in this manner then it's unnecessarily costly to always copy the stack frame. Some more advanced lexing might be possible to determine when this is necessary but I think the response was "maybe but not right now." One slightly lame fix that might work in a single-threaded environment is: dg_t fn(int a ) { static int cpy = a; int inner() { int i = cpy; return i + 5; } dg_t result = &inner; return result; } I suppose if this works you could use thread local storage to do the same thing in a multithreaded program. Sean
Jul 21 2004
Russ Lewis wrote:I am a big proponent of having some functionality that enables us to copy stack delegates. We don't want copying to be the default, however. Consider this code: int foo() { int counter = 0; bar(void delegate(int val) { counter += arg; }); return counter; } void bar(void delegate() dg) { foreach(whatever) dg(something); } You see, in this code, you want to give the delegate the capability to modify the real values on the stack. You don't want it to be using a duplicate. So what I've advocated is that we have the option to copy or not. Sean Kelly wrote:I assume you meant to write 'counter += val'. Actually that seems an error in my definition of a closure. In the above situation the behavior I would expect from the compiler is all accessible variables not passed as params are *in*, so counter would have to be redeclared within the inner function. Pseudo example of what I am thinking of(internals): struct delegate { int size; void * data; void *funct; } Assigning a function to a delegate would mean ensuring the size/data parameters are valid. The only problem that remains would be giving a delegate function access to the *data* within the struct. The compiler could: Have code inserted before and after a delegate is called to handle copying the data to and from the stack(as mentioned). Performance is extremely minimal because in most cases its just a few Push/Pops. -or- For each non-static function, have another version internally(AST) which is used when an assignment to a delegate happens. The delegate version accesses local variables in its storage space instead of the stack(using the data*). Having a different version probably wouldnt be that bad, templates do this already. I realize this would be a PITA, but this would allow many functional idioms to be used. Sorry for the rambling, I hope this makes some sense.
Jul 22 2004
David Medlock wrote:Russ Lewis wrote:Yes. and I also noticed that the argument to bar() has the wrong argument type. My bad. :(I am a big proponent of having some functionality that enables us to copy stack delegates. We don't want copying to be the default, however. Consider this code: int foo() { int counter = 0; bar(void delegate(int val) { counter += arg; }); return counter; } void bar(void delegate() dg) { foreach(whatever) dg(something); }I assume you meant to write 'counter += val'.Actually that seems an error in my definition of a closure. In the above situation the behavior I would expect from the compiler is all accessible variables not passed as params are *in*, so counter would have to be redeclared within the inner function.I see what you're thinking of, and it makes sense. However, what I'm pointing out is that what you're suggesting is only appropriate in some designs. In your design, the creator of the delegate cannot get any results out of the delegate (unless, of course, you pass pointers into the delegate). What I was pointing out is that there are two different paradigms here. One paradigm is supported by D currently; you are saying that it should be something else. I would like the compiler to support both paradigms.Pseudo example of what I am thinking of(internals): struct delegate { int size; void * data; void *funct; }Essentially, a stack delegate is syntax sugar for declaring a new struct type. Here's old C++ code: struct func_workingData { int counter; } int func() { func_workingData data; data.counter = 0; bar(&func_inner_delegate, &data); return data.counter; } void func_inner_delegate(void *arg, int val) { func_workingData *this = (func_workingData*)arg; this.counter += val; } void bar(void (*callback)(void*,int), void *arg) { for(<whatever>) callback(arg, <something>); } So you see, when you create a stack delegate you are essentially passing a pointer to an anonymous struct on the stack. When you duplicate, you are doing something else: void func2() { func_workingData data; data.counter = 0; func_workingData *copy = malloc(sizeof(data)); memcpy(copy, &data, sizeof(data)); bar(&func_inner_delegate, copy); } Note that there's no need to push/pop the duplicated stack variables. You can simply access them directly from the heap. The delegate code doesn't need to know whether the pointer points to the heap or the stack.
Jul 22 2004
If delegates are allowed to point at the stack they were instatiated within then the compiler has to be smart enough to leave the stack around for the garbage collector to clean up when the delegate is finally done away with. This seems strange... Otherwise you'll end up with stackless delegates.. In article <cdoc6l$mkg$1 digitaldaemon.com>, David Medlock says...Russ Lewis wrote:I am a big proponent of having some functionality that enables us to copy stack delegates. We don't want copying to be the default, however. Consider this code: int foo() { int counter = 0; bar(void delegate(int val) { counter += arg; }); return counter; } void bar(void delegate() dg) { foreach(whatever) dg(something); } You see, in this code, you want to give the delegate the capability to modify the real values on the stack. You don't want it to be using a duplicate. So what I've advocated is that we have the option to copy or not. Sean Kelly wrote:I assume you meant to write 'counter += val'. Actually that seems an error in my definition of a closure. In the above situation the behavior I would expect from the compiler is all accessible variables not passed as params are *in*, so counter would have to be redeclared within the inner function. Pseudo example of what I am thinking of(internals): struct delegate { int size; void * data; void *funct; } Assigning a function to a delegate would mean ensuring the size/data parameters are valid. The only problem that remains would be giving a delegate function access to the *data* within the struct. The compiler could: Have code inserted before and after a delegate is called to handle copying the data to and from the stack(as mentioned). Performance is extremely minimal because in most cases its just a few Push/Pops. -or- For each non-static function, have another version internally(AST) which is used when an assignment to a delegate happens. The delegate version accesses local variables in its storage space instead of the stack(using the data*). Having a different version probably wouldnt be that bad, templates do this already. I realize this would be a PITA, but this would allow many functional idioms to be used. Sorry for the rambling, I hope this makes some sense.
Jul 22 2004
Stack delegates work just like any code that passes pointers to stack variables: they are valid only as long as the function doesn't return, or leave the block of code where the variable resides. So this code is perfectly ok in C/C++ and D: void foo() { int var = 0; bar(&var); } so long as bar() doesn't save the pointer. bar() should never use the pointer after it returns to foo(). Likewise, a stack delegate is valid only as long as the stack frame is valid. So you're right, you can end up with stackless delegates...but this is just the way they work. The programmer has to be aware of it. Of course, this whole discussion is about the fact that this would not be an issue if it were possible to have stack delegates which use copies of the stack. Sha Chancellor wrote:If delegates are allowed to point at the stack they were instatiated within then the compiler has to be smart enough to leave the stack around for the garbage collector to clean up when the delegate is finally done away with. This seems strange... Otherwise you'll end up with stackless delegates..
Jul 22 2004
In article <cdpbel$14k4$1 digitaldaemon.com>, Russ Lewis says...Stack delegates work just like any code that passes pointers to stack variables: they are valid only as long as the function doesn't return, or leave the block of code where the variable resides. So this code is perfectly ok in C/C++ and D: void foo() { int var = 0; bar(&var); } so long as bar() doesn't save the pointer. bar() should never use the pointer after it returns to foo(). Likewise, a stack delegate is valid only as long as the stack frame is valid. So you're right, you can end up with stackless delegates...but this is just the way they work. The programmer has to be aware of it. Of course, this whole discussion is about the fact that this would not be an issue if it were possible to have stack delegates which use copies of the stack.Here's a few thoughts: what happens when a delegate, executing on it's own stack-frame (copied into GC'd memory for argument's sake) calls another function? Would the called function's stack frame be allocated back on the thread's stack (provided it's not another stack delegate)? How would recursion within a stack-delegate be handled? ...on the thread's stack in all cases, perhaps? The reason why I bring this up is that you may be able to create a full-on reference implementation in D itself (with some liberal application of assembler of course). All you'd need to to is push the stack-segment and stack-pointer onto the current stack for safe-keeping before setting it to what stack you're going to use (this means that whatever stack space the delegate needs would have to be at least 16 bytes to accomodate this). This would apply to entering the stack-delegate as well as calling functions that lead out of it. - Pragma
Jul 22 2004
pragma wrote:Here's a few thoughts: what happens when a delegate, executing on it's own stack-frame (copied into GC'd memory for argument's sake) calls another function? Would the called function's stack frame be allocated back on the thread's stack (provided it's not another stack delegate)? How would recursion within a stack-delegate be handled? ...on the thread's stack in all cases, perhaps? The reason why I bring this up is that you may be able to create a full-on reference implementation in D itself (with some liberal application of assembler of course). All you'd need to to is push the stack-segment and stack-pointer onto the current stack for safe-keeping before setting it to what stack you're going to use (this means that whatever stack space the delegate needs would have to be at least 16 bytes to accomodate this). This would apply to entering the stack-delegate as well as calling functions that lead out of it.I think that perhaps I miscommunicated here. I don't think that the stack frame of a delegate should ever reside on the heap. Instead, I think that some of the local variables of a certain delegate should reside inside a struct which resides on the heap. The difference may be small for the programmer, but the difference is massive for implementation. I'm going to try to illustrate. Please forgive the amount of hex is will require... int delegate() foo() { int counter; return stack_frame.dup.delegate int(int val) { counter += val; return val; } } void bar() { int delegate() del = foo(); foreach(int i; <whatever>) printf("Value = %d, retval = %d\n", i,del(i)); } Ok, so the code here has a stack delegate which gets duplicated. What happens when bar() calls that delegate? This is how I imagine the stack would look: ADDRESS 0xB080 (STACK) Local variable 'del' (8 bytes) obj = 0x8000 fptr = 0x4000 Local variable 'i' (4 bytes) ADDRESS 0xB070 (STACK) Argument 'this' (4 bytes) ptr = 0x8000 /* note: this is the 'obj' from above */ ADDRESS 0x8000 (HEAP) Variable 'counter' The point I'm trying to communicate here is that the stack frame for the delegate resides on the same stack as everybody else. That stack frame includes a pointer variable which points to a location in the heap where copied variables (things that USED TO BE on the stack) now reside. A delegate can then call any other function it wants to. It continues to use the stack just like any other function does. No magic necessary. You can see how you can implement this in one of my previous posts in this thread. All you have to do is to create a struct type, and enclose all of the copied stack variables in a struct of that type.
Jul 22 2004
Forgive me Russ for not quite following the reasoning behind this. Would you mind clarifying why this delegate example is so much more valuable that, say, foo() returning an Interface or even a base-class? I mean, wouldn't something like this provide the equivalent functionality? IDelegate foo() { class Foo : IDelegate { int counter; dg(int val) {counter += val; return counter;} } return new Foo; } etc. etc. Certainly, there's a memory allocation there; but that cost can be avoided at runtime through various means. What's the important bit I'm missing? (yeah, yeah; apart from a brain ... :~) - Kris "Russ Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message news:cdq7ii$1ho2$1 digitaldaemon.com...pragma wrote:ownHere's a few thoughts: what happens when a delegate, executing on it'sthestack-frame (copied into GC'd memory for argument's sake) calls another function? Would the called function's stack frame be allocated back onrecursionthread's stack (provided it's not another stack delegate)? How wouldcases,within a stack-delegate be handled? ...on the thread's stack in allfull-onperhaps? The reason why I bring this up is that you may be able to create aassemblerreference implementation in D itself (with some liberal application ofstack-pointerof course). All you'd need to to is push the stack-segment andyou'reonto the current stack for safe-keeping before setting it to what stackwould havegoing to use (this means that whatever stack space the delegate needsentering theto be at least 16 bytes to accomodate this). This would apply tostack-delegate as well as calling functions that lead out of it.I think that perhaps I miscommunicated here. I don't think that the stack frame of a delegate should ever reside on the heap. Instead, I think that some of the local variables of a certain delegate should reside inside a struct which resides on the heap. The difference may be small for the programmer, but the difference is massive for implementation. I'm going to try to illustrate. Please forgive the amount of hex is will require... int delegate() foo() { int counter; return stack_frame.dup.delegate int(int val) { counter += val; return val; } } void bar() { int delegate() del = foo(); foreach(int i; <whatever>) printf("Value = %d, retval = %d\n", i,del(i)); } Ok, so the code here has a stack delegate which gets duplicated. What happens when bar() calls that delegate? This is how I imagine the stack would look: ADDRESS 0xB080 (STACK) Local variable 'del' (8 bytes) obj = 0x8000 fptr = 0x4000 Local variable 'i' (4 bytes) ADDRESS 0xB070 (STACK) Argument 'this' (4 bytes) ptr = 0x8000 /* note: this is the 'obj' from above */ ADDRESS 0x8000 (HEAP) Variable 'counter' The point I'm trying to communicate here is that the stack frame for the delegate resides on the same stack as everybody else. That stack frame includes a pointer variable which points to a location in the heap where copied variables (things that USED TO BE on the stack) now reside. A delegate can then call any other function it wants to. It continues to use the stack just like any other function does. No magic necessary. You can see how you can implement this in one of my previous posts in this thread. All you have to do is to create a struct type, and enclose all of the copied stack variables in a struct of that type.
Jul 22 2004
Kris wrote:Forgive me Russ for not quite following the reasoning behind this. Would you mind clarifying why this delegate example is so much more valuable that, say, foo() returning an Interface or even a base-class? I mean, wouldn't something like this provide the equivalent functionality? IDelegate foo() { class Foo : IDelegate { int counter; dg(int val) {counter += val; return counter;} } return new Foo; } etc. etc. Certainly, there's a memory allocation there; but that cost can be avoided at runtime through various means. What's the important bit I'm missing? (yeah, yeah; apart from a brain ... :~) - Kris "Russ Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message news:cdq7ii$1ho2$1 digitaldaemon.com...This is what I meant above: "For each non-static function, have another version internally(AST) which is used when an assignment to a delegate happens. The delegate version accesses local variables in its storage space instead of the stack(using the data*). Having a different version probably wouldnt be that bad, templates do this already." The delegate version would not change local variables on the stack, but as dereferenced through a pointer on the stack. Doing this requires that an assignment of a function to a delegate ensures memory is set aside, whereas assigning a delegate to another just copies the data(struct mentioned above) from the delegate.pragma wrote:ownHere's a few thoughts: what happens when a delegate, executing on it'sthestack-frame (copied into GC'd memory for argument's sake) calls another function? Would the called function's stack frame be allocated back onrecursionthread's stack (provided it's not another stack delegate)? How wouldcases,within a stack-delegate be handled? ...on the thread's stack in allfull-onperhaps? The reason why I bring this up is that you may be able to create aassemblerreference implementation in D itself (with some liberal application ofstack-pointerof course). All you'd need to to is push the stack-segment andyou'reonto the current stack for safe-keeping before setting it to what stackwould havegoing to use (this means that whatever stack space the delegate needsentering theto be at least 16 bytes to accomodate this). This would apply tostack-delegate as well as calling functions that lead out of it.I think that perhaps I miscommunicated here. I don't think that the stack frame of a delegate should ever reside on the heap. Instead, I think that some of the local variables of a certain delegate should reside inside a struct which resides on the heap. The difference may be small for the programmer, but the difference is massive for implementation. I'm going to try to illustrate. Please forgive the amount of hex is will require... int delegate() foo() { int counter; return stack_frame.dup.delegate int(int val) { counter += val; return val; } } void bar() { int delegate() del = foo(); foreach(int i; <whatever>) printf("Value = %d, retval = %d\n", i,del(i)); } Ok, so the code here has a stack delegate which gets duplicated. What happens when bar() calls that delegate? This is how I imagine the stack would look: ADDRESS 0xB080 (STACK) Local variable 'del' (8 bytes) obj = 0x8000 fptr = 0x4000 Local variable 'i' (4 bytes) ADDRESS 0xB070 (STACK) Argument 'this' (4 bytes) ptr = 0x8000 /* note: this is the 'obj' from above */ ADDRESS 0x8000 (HEAP) Variable 'counter' The point I'm trying to communicate here is that the stack frame for the delegate resides on the same stack as everybody else. That stack frame includes a pointer variable which points to a location in the heap where copied variables (things that USED TO BE on the stack) now reside. A delegate can then call any other function it wants to. It continues to use the stack just like any other function does. No magic necessary. You can see how you can implement this in one of my previous posts in this thread. All you have to do is to create a struct type, and enclose all of the copied stack variables in a struct of that type.
Jul 23 2004
Kris wrote:Forgive me Russ for not quite following the reasoning behind this. Would you mind clarifying why this delegate example is so much more valuable that, say, foo() returning an Interface or even a base-class? I mean, wouldn't something like this provide the equivalent functionality? IDelegate foo() { class Foo : IDelegate { int counter; dg(int val) {counter += val; return counter;} } return new Foo; }A class provides equivalent functionality, but, as you noted, it requires much more overhead. Plus, I would like to declare the whole duplicated stack delegate on a single line, rather than having the complexity and readability issues of having to declare a whole class or struct just for this purpose. Incidentally, when you are only using a single interface function (and you don't need to do anything to the object itself), delegates are always more flexible than interfaces. They can interact with other classes, which don't share any common base class or interface, and don't require the declaration of some additional Interface. So I'd prefer this code, if I was going to use a class: int delegate() foo() { class Foo { int counter; this() { counter = 0; } int dg(int val) { counter += val; return counter; } } Foo ret = new Foo; return &ret.dg; }
Jul 23 2004
Thanks! Sure, delegates are great for certain tasks. I guess my fear is trying to make them do things that might perhaps be better off approached from the other direction (a syntax and methodology for non-verbose & low-cost class instantiation). The latter would expose the same benefits you propose, yet support more than one callback and provide arguably better encapsulation. Just an observation though. What you propose is certainly interesting. - Kris "Russ Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message news:cdr9gr$23j0$1 digitaldaemon.com...Kris wrote:youForgive me Russ for not quite following the reasoning behind this. Wouldmind clarifying why this delegate example is so much more valuable that, say, foo() returning an Interface or even a base-class? I mean, wouldn't something like this provide the equivalent functionality? IDelegate foo() { class Foo : IDelegate { int counter; dg(int val) {counter += val; return counter;} } return new Foo; }A class provides equivalent functionality, but, as you noted, it requires much more overhead. Plus, I would like to declare the whole duplicated stack delegate on a single line, rather than having the complexity and readability issues of having to declare a whole class or struct just for this purpose. Incidentally, when you are only using a single interface function (and you don't need to do anything to the object itself), delegates are always more flexible than interfaces. They can interact with other classes, which don't share any common base class or interface, and don't require the declaration of some additional Interface. So I'd prefer this code, if I was going to use a class: int delegate() foo() { class Foo { int counter; this() { counter = 0; } int dg(int val) { counter += val; return counter; } } Foo ret = new Foo; return &ret.dg; }
Jul 23 2004
Kris wrote:Thanks! Sure, delegates are great for certain tasks. I guess my fear is trying to make them do things that might perhaps be better off approached from the other direction (a syntax and methodology for non-verbose & low-cost class instantiation). The latter would expose the same benefits you propose, yet support more than one callback and provide arguably better encapsulation. Just an observation though. What you propose is certainly interesting.I agree that it would be nice to have some mechanism for very low-cost classes. Whether that is a "better" approach or not...well, we'll probably not know until they are available! :) It has occurred to me before that, if a single-function interface is just a delegate, then isn't a multifunction interface just a struct of delegates, sort of? This interface: interface FooI { int foo(); void bar(int); char baz(char[]); } is a lot like this struct: struct FooS { int delegate() foo; void delegate(int) bar; char delegate(char[]) baz; void delegate() do_delete; /* deletes the object */ Object delegate() getObj; /* gets the obj pointer, so you can cast it */ } so you could do this: class MyClass { int asdf() {...} void jkl(int) {...} char qwerty(char[]) {...} ~this() {...} // this function would be unnecessary if // stack duplicate delegates were implemented Object getMe() { return this; } } FooS *GetFooS(MyClass obj) { FooS *ret = new FooS[1]; ret.foo = &obj.asdf; ret.bar = &obj.jkl; ret.baz = &obj.qwerty; ret.do_delete = &.obj~this; ret.getObj = &obj.getMe; return ret; } So a struct of delegates is more adaptible than an interface...the names in the class don't have to match the names in the interface. Of course, this sort of thing has a couple of big disadvantages: * Size. Since each ptr is a delegate, it takes 8 bytes. Plus, each object has to have its own struct...no shared vtbl * Hard to declare However, if we all agreed that this sort of thing was useful, then it might be possible to devise (for 2.0) some new, better solution for this.
Jul 23 2004
In article <cdn6a7$76i$1 digitaldaemon.com>, Russ Lewis says...I am a big proponent of having some functionality that enables us to copy stack delegates. We don't want copying to be the default, however.I agree. The only reason I suggested that was because it would allow us to use the existing "auto" keyword in a consistent manner. Sean
Jul 22 2004
Sean Kelly wrote:In article <cdn6a7$76i$1 digitaldaemon.com>, Russ Lewis says...I apologize, but I'm clueless about how 'auto' comes in here. Can you give an example? Sounds interesting.I am a big proponent of having some functionality that enables us to copy stack delegates. We don't want copying to be the default, however.I agree. The only reason I suggested that was because it would allow us to use the existing "auto" keyword in a consistent manner.
Jul 22 2004
In article <cdp115$vo6$1 digitaldaemon.com>, Russ Lewis says...Sean Kelly wrote:It was in an earlier post in this thread. Here's an example: In the above code, d1 will get a copy of the stack frame that it can carry outside of func, while d2 will not since it is declared as "auto." This seems consistent with the existing auto semantics for variables. Only catch is that, as you said, I think the default behavior should remain how it is. SeanIn article <cdn6a7$76i$1 digitaldaemon.com>, Russ Lewis says...I apologize, but I'm clueless about how 'auto' comes in here. Can you give an example? Sounds interesting.I am a big proponent of having some functionality that enables us to copy stack delegates. We don't want copying to be the default, however.I agree. The only reason I suggested that was because it would allow us to use the existing "auto" keyword in a consistent manner.
Jul 22 2004
Sean Kelly wrote:In the above code, d1 will get a copy of the stack frame that it can carry outside of func, while d2 will not since it is declared as "auto." This seems consistent with the existing auto semantics for variables. Only catch is that, as you said, I think the default behavior should remain how it is.Ok, I see what you're saying. The syntax makes a lot of sense, except that I would prefer not to overload the "auto" keyword. Maybe I'm the only one, but it didn't make immediate sense to me. Remember how the keyword "static" got overloaded in C...ick. How would you declare a delegate literal using the above syntax, however? Would you declare void delegate() foo = auto delegate void() {...} ? Ok, weird ponder here. I've been playing with the idea that it would be nice to declare a delegate that has any arbitrary 'this' pointer, using the following syntax: MyClass foo = <whatever>; void delegate() del = foo.delegate void() { foo.x(); foo.y(); } The cool thing is, I just realized that that syntax makes stack duplicates easier to create: void foo() { void delegate() del = stack_frame.dup.delegate void() { ... } } Then, you realize that the current stack delegate syntax can be viewed as syntax sugar: void bar() { void delegate() d1 = delegate void() {...} void delegate() d2 = stack_frame.delegate void() {...} } In the code above, both delegate literals are non-copying delegates. Thoughts?
Jul 22 2004
Russ Lewis wrote:Sean Kelly wrote:Interesting. I think you could almost make this work with templates alone(using a class and opCmd) if function themselves had a *.size* property to reveal the amount of stack space needed for the functions variables. On first call of the function the space is allocated/initialized, with subsequent calls just referring to the stack frame. I know the behavior I would like to use delegates for: like that in lua/scheme/Javascript (true closures). It is just a hard feature to add with C because functions are not created, just declared. When you specify 'static int a' within a inner function, static really means 'singular' because no matter where it is accessed, there is only one. If delegates were forced to be created like other objects, implementing the underlying semantics would probably be very simple.In the above code, d1 will get a copy of the stack frame that it can carry outside of func, while d2 will not since it is declared as "auto." This seems consistent with the existing auto semantics for variables. Only catch is that, as you said, I think the default behavior should remain how it is.Ok, I see what you're saying. The syntax makes a lot of sense, except that I would prefer not to overload the "auto" keyword. Maybe I'm the only one, but it didn't make immediate sense to me. Remember how the keyword "static" got overloaded in C...ick. How would you declare a delegate literal using the above syntax, however? Would you declare void delegate() foo = auto delegate void() {...} ? Ok, weird ponder here. I've been playing with the idea that it would be nice to declare a delegate that has any arbitrary 'this' pointer, using the following syntax: MyClass foo = <whatever>; void delegate() del = foo.delegate void() { foo.x(); foo.y(); } The cool thing is, I just realized that that syntax makes stack duplicates easier to create: void foo() { void delegate() del = stack_frame.dup.delegate void() { ... } } Then, you realize that the current stack delegate syntax can be viewed as syntax sugar: void bar() { void delegate() d1 = delegate void() {...} void delegate() d2 = stack_frame.delegate void() {...} } In the code above, both delegate literals are non-copying delegates. Thoughts?
Jul 22 2004
In article <cdp2td$10ib$1 digitaldaemon.com>, Russ Lewis says...How would you declare a delegate literal using the above syntax, however? Would you declare void delegate() foo = auto delegate void() {...}Yeah I suppose. And this does look a tad weird.Ok, weird ponder here. I've been playing with the idea that it would be nice to declare a delegate that has any arbitrary 'this' pointer, using the following syntax: MyClass foo = <whatever>; void delegate() del = foo.delegate void() { foo.x(); foo.y(); }Interesting idea. The only weird thing here is that this feature would enable a user to break encapsulation rules by accessing hidden members via these delegates.Then, you realize that the current stack delegate syntax can be viewed as syntax sugar: void bar() { void delegate() d1 = delegate void() {...} void delegate() d2 = stack_frame.delegate void() {...} } In the code above, both delegate literals are non-copying delegates.Interesting idea. Never thought of nesting keyowrds in this manner. I'm not sure if I like it though, I'll have to think about it for a bit. Am I right in assuming that stack_frame is a keyword as well and not a type sitting off somewhere? Sean
Jul 22 2004
Sean Kelly wrote:You've confused me again here. Assuming that the code in the delegate can't access anything that the function can access, how does this break encapsulation? You can already sort of do this in D, although it requires you to create a wrapper class that has no purpose other than to give you a delegate. You can do this in D using the following code: MyClass foo = <whatever>; void hand_coded_delegate(void *arg) { MyClass foo = cast(MyClass)arg; foo.x(); foo.y(); } union { void delegate() del; struct { void *arg; void function(void*) fptr; } } convert; convert.arg = cast(void*)foo; convert.fptr = &hand_coded_delegate; void delegate() del = convert.del; So you see, the delegate can't do anything that the enclosing block can't do.Ok, weird ponder here. I've been playing with the idea that it would be nice to declare a delegate that has any arbitrary 'this' pointer, using the following syntax: MyClass foo = <whatever>; void delegate() del = foo.delegate void() { foo.x(); foo.y(); }Interesting idea. The only weird thing here is that this feature would enable a user to break encapsulation rules by accessing hidden members via these delegates.Yes. My assumption is that stack_frame is a new keyword, which is a pointer to an anonymous structure type which models the local function variables. It's sort of like 'this' for a stack frame.Then, you realize that the current stack delegate syntax can be viewed as syntax sugar: void bar() { void delegate() d1 = delegate void() {...} void delegate() d2 = stack_frame.delegate void() {...} } In the code above, both delegate literals are non-copying delegates.Interesting idea. Never thought of nesting keyowrds in this manner. I'm not sure if I like it though, I'll have to think about it for a bit. Am I right in assuming that stack_frame is a keyword as well and not a type sitting off somewhere?
Jul 22 2004
In article <cdp6ms$12g3$1 digitaldaemon.com>, Russ Lewis says...Sean Kelly wrote:Oh I misunderstood. When you said "hidden this pointer" I assumed you meant that the delegate would behave as if it were a first class member of foo. Interesting idea. SeanYou've confused me again here. Assuming that the code in the delegate can't access anything that the function can access, how does this break encapsulation?Ok, weird ponder here. I've been playing with the idea that it would be nice to declare a delegate that has any arbitrary 'this' pointer, using the following syntax: MyClass foo = <whatever>; void delegate() del = foo.delegate void() { foo.x(); foo.y(); }Interesting idea. The only weird thing here is that this feature would enable a user to break encapsulation rules by accessing hidden members via these delegates.
Jul 22 2004
I have a few friendly questions. :) I've followed the conversation so far, but one thing still escapes me: what would such a closure accomplish that a class with opCall cannot? Is this trying to achieve some kind of efficency gain or just a better syntax (or both)? In article <cdp5cu$11ui$1 digitaldaemon.com>, Sean Kelly says...Personally, I like how succinct this concept is. I have the same question: is 'stack_frame' a new keyword or some type? If it is keyword, wouldn't overloading 'new' be a little more appropriate, since it's getting stack frame all to itself?Then, you realize that the current stack delegate syntax can be viewed as syntax sugar: void bar() { void delegate() d1 = delegate void() {...} void delegate() d2 = stack_frame.delegate void() {...} } In the code above, both delegate literals are non-copying delegates.Interesting idea. Never thought of nesting keyowrds in this manner. I'm not sure if I like it though, I'll have to think about it for a bit. Am I right in assuming that stack_frame is a keyword as well and not a type sitting off somewhere?void bar() { void delegate() d1 = delegate void() {...} void delegate() d2 = new delegate void() {...} }
Jul 22 2004
pragma wrote:I have a few friendly questions. :) I've followed the conversation so far, but one thing still escapes me: what would such a closure accomplish that a class with opCall cannot? Is this trying to achieve some kind of efficency gain or just a better syntax (or both)?It's both. If you implement this sort of functionality with a class with opCall, you have an unnecessary level of indirection; the 'this' argument to the delegate will be a pointer to your wrapper class, and you have to then read a member variable of the class to get the pointer to 'foo'. Worse, you have to allocate, construct, and then later garbage collect a new class object on the heap. In my reply to Sean, I wrote some D code that avoids all of these inefficiencies. However, it's many lines of code. So I'm suggesting that it would be good to use my syntax as it is far cleaner, easier-to-read code.In article <cdp5cu$11ui$1 digitaldaemon.com>, Sean Kelly says...This might make a lot of sense. To its favor, it makes more explicit that new memory is being allocated (although using .dup is pretty explicit, as well). One strike against it, however, is that it doesn't make immediately clear that something is being copied. New typically allocates uninitialized data.Personally, I like how succinct this concept is. I have the same question: is 'stack_frame' a new keyword or some type? If it is keyword, wouldn't overloading 'new' be a little more appropriate, since it's getting stack frame all to itself?Then, you realize that the current stack delegate syntax can be viewed as syntax sugar: void bar() { void delegate() d1 = delegate void() {...} void delegate() d2 = stack_frame.delegate void() {...} } In the code above, both delegate literals are non-copying delegates.Interesting idea. Never thought of nesting keyowrds in this manner. I'm not sure if I like it though, I'll have to think about it for a bit. Am I right in assuming that stack_frame is a keyword as well and not a type sitting off somewhere?void bar() { void delegate() d1 = delegate void() {...} void delegate() d2 = new delegate void() {...} }
Jul 22 2004
In article <cdp6ub$12kh$1 digitaldaemon.com>, pragma <EricAnderton at yahoo dot com> says...I have a few friendly questions. :) I've followed the conversation so far, but one thing still escapes me: what would such a closure accomplish that a class with opCall cannot? Is this trying to achieve some kind of efficency gain or just a better syntax (or both)?Nothing really, but for some additional simplicity. A returned class needs its own external declaration while delegates can be declared as a part of the function prototype. Also, returning a class would require the programmer to manually copy the data he cares about while this dynamic closure idea would make the process automatic. Seam
Jul 22 2004
"pragma" <EricAnderton at yahoo dot compragma_member pathlink.com> wrote in message news:cdp6ub$12kh$1 digitaldaemon.com...I have a few friendly questions. :) I've followed the conversation so far, but one thing still escapes me:whatwould such a closure accomplish that a class with opCall cannot? Is thistryingto achieve some kind of efficency gain or just a better syntax (or both)?I tryed to folow you conversation. And now I have only one wish. Pls, could someone of you add some of this (or other) useful/essential examples to the : dsource.org/tutorial / delegates section (or Wiki)? I think many people will be grateful to you (especially me). Thanks. ----- Yuriy.
Jul 23 2004
I posted an example, but need to edit it. How do I do that? Blandger wrote:"pragma" <EricAnderton at yahoo dot compragma_member pathlink.com> wrote in message news:cdp6ub$12kh$1 digitaldaemon.com...I have a few friendly questions. :) I've followed the conversation so far, but one thing still escapes me:whatwould such a closure accomplish that a class with opCall cannot? Is thistryingto achieve some kind of efficency gain or just a better syntax (or both)?I tryed to folow you conversation. And now I have only one wish. Pls, could someone of you add some of this (or other) useful/essential examples to the : dsource.org/tutorial / delegates section (or Wiki)? I think many people will be grateful to you (especially me). Thanks. ----- Yuriy.
Jul 26 2004
"Russ Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message news:ce3u1v$1h7j$1 digitaldaemon.com...I posted an example, but need to edit it. How do I do that?Sorry I didn't understand a question. I'd like to see good delegete examples in the dsource.org -> tutorial section. To post it you can apply to Brad. He is admin there 'brad at dsource dot org' You can post them in the appropriate D Wiki page. Though I don't have a experience using it :( ---- Yuriy.
Jul 26 2004
Blandger wrote:"Russ Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message news:ce3u1v$1h7j$1 digitaldaemon.com...I have posted 4 examples. See http://dsource.org/tutorials/index.php?show_topic=Delegates I'll email Brad to ask him how to fix that one broken title...I posted an example, but need to edit it. How do I do that?Sorry I didn't understand a question. I'd like to see good delegete examples in the dsource.org -> tutorial section. To post it you can apply to Brad. He is admin there 'brad at dsource dot org'
Jul 27 2004
"Russ Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message news:ce6230$2e0l$1 digitaldaemon.com...I have posted 4 examples. See http://dsource.org/tutorials/index.php?show_topic=Delegates I'll email Brad to ask him how to fix that one broken title...Thanks !! ----- Yuriy.
Jul 27 2004