www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - I implemented delegates in D

reply maik klein <maikklein googlemail.com> writes:
I am currently writing a task system and I have this case where I 
want to send a delegate to a different thread but this delegate 
also captures a variable. I use this to implement a "future".

Now as far as I know this delegate will allocate GC memory and I 
just wanted to avoid that, just for fun.

Here is the code https://dpaste.dzfl.pl/cd77fce99a5b

I have only worked on it a couple of hours and I am sure there 
are many problems with it.

Basically the idea is that you can use normal lambda syntax. If 
you want a function that returns and int and takes an int, you 
can write it like this:

(int i) => i

If you want a function that returns an int, takes an int, but 
also captures and int you would write it like this

(int i, int captured) => i + captured

But you also have to declare the base function type without the 
captured variables beforehand.

Fn!(FnType!(int, int), (int i, int captured) => i + captured)(42);

That is how I know what the captured variables are.

The only part that is currently missing are polymorphic 
delegates. They are not too useful if I can't pack them into the 
same array.

I guess I have to do this with classes/interfaces.

Thoughts?

Has this been done before?
Jun 09 2016
next sibling parent reply Alex Parrill <initrd.gz gmail.com> writes:
On Thursday, 9 June 2016 at 21:02:26 UTC, maik klein wrote:
 Has this been done before?
Well, yes, the entire point of delegates is to be able to capture variables (as opposed to function pointers, which cannot). auto createADelegate(int captured) { return (int a) => captured + a; } void main() { auto dg1 = createADelegate(5); auto dg2 = createADelegate(32); assert(dg1(5) == 10); assert(dg1(10) == 15); assert(dg2(8) == 40); assert(dg2(32) == 64); } https://dpaste.dzfl.pl/90ebc29651f6 (Unfortunately template delegates, like the ones used with map, don't keep their captured variables alive after the captured variables go out of scope, but it doesn't sound like you need those)
Jun 09 2016
parent reply maik klein <maikklein googlemail.com> writes:
On Thursday, 9 June 2016 at 21:32:33 UTC, Alex Parrill wrote:
 On Thursday, 9 June 2016 at 21:02:26 UTC, maik klein wrote:
 Has this been done before?
Well, yes, the entire point of delegates is to be able to capture variables (as opposed to function pointers, which cannot). auto createADelegate(int captured) { return (int a) => captured + a; } void main() { auto dg1 = createADelegate(5); auto dg2 = createADelegate(32); assert(dg1(5) == 10); assert(dg1(10) == 15); assert(dg2(8) == 40); assert(dg2(32) == 64); } https://dpaste.dzfl.pl/90ebc29651f6 (Unfortunately template delegates, like the ones used with map, don't keep their captured variables alive after the captured variables go out of scope, but it doesn't sound like you need those)
I meant, "has this been implement as a library before". I am well aware that delegates exist in the language but as far as I know you can not do manual allocation with delegates (to avoid the GC).
Jun 09 2016
parent reply Mathias Lang via Digitalmars-d <digitalmars-d puremagic.com> writes:
To avoid the delegate being GC allocated, use `scope foo = (int i) { ... }`
at call site.
You can also make your function signature as `void func(scope void
delegate() dg)` in which case it won't allocate if you pass a literal
directly.

2016-06-09 23:57 GMT+02:00 maik klein via Digitalmars-d <
digitalmars-d puremagic.com>:

 On Thursday, 9 June 2016 at 21:32:33 UTC, Alex Parrill wrote:

 On Thursday, 9 June 2016 at 21:02:26 UTC, maik klein wrote:

 Has this been done before?
Well, yes, the entire point of delegates is to be able to capture variables (as opposed to function pointers, which cannot). auto createADelegate(int captured) { return (int a) => captured + a; } void main() { auto dg1 = createADelegate(5); auto dg2 = createADelegate(32); assert(dg1(5) == 10); assert(dg1(10) == 15); assert(dg2(8) == 40); assert(dg2(32) == 64); } https://dpaste.dzfl.pl/90ebc29651f6 (Unfortunately template delegates, like the ones used with map, don't keep their captured variables alive after the captured variables go out of scope, but it doesn't sound like you need those)
I meant, "has this been implement as a library before". I am well aware that delegates exist in the language but as far as I know you can not do manual allocation with delegates (to avoid the GC).
Jun 09 2016
next sibling parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 6/9/16 6:06 PM, Mathias Lang via Digitalmars-d wrote:
 To avoid the delegate being GC allocated, use `scope foo = (int i) { ...
 }` at call site.
Is that true? At one point in D's past, this ONLY worked if you passed a delegate to a function accepting a scope delegate. Maybe it's been fixed. -Steve
Jun 09 2016
next sibling parent Mathias Lang via Digitalmars-d <digitalmars-d puremagic.com> writes:
It's even part of DMD testsuite:
https://github.com/dlang/dmd/blob/38c1bad2f41ad8e6bc7c08deae26e30c9c7d7704/test/runnable/nogc.d#L20

At DConf, Dicebot mention that he had some problems in the past and it
doesn't work in some places. The Weka people seemed to have similar
experience.
I did some experiment and so far the only related issue I've found is
https://issues.dlang.org/show_bug.cgi?id=16037 . If you happen to come
accross a case where it doesn't work, I would very like to be CC'ed on that
issue :)

2016-06-10 0:17 GMT+02:00 Steven Schveighoffer via Digitalmars-d <
digitalmars-d puremagic.com>:

 On 6/9/16 6:06 PM, Mathias Lang via Digitalmars-d wrote:

 To avoid the delegate being GC allocated, use `scope foo = (int i) { ...
 }` at call site.
Is that true? At one point in D's past, this ONLY worked if you passed a delegate to a function accepting a scope delegate. Maybe it's been fixed. -Steve
Jun 09 2016
prev sibling parent Martin Nowak <code+news.digitalmars dawg.eu> writes:
On 06/10/2016 12:17 AM, Steven Schveighoffer wrote:
 On 6/9/16 6:06 PM, Mathias Lang via Digitalmars-d wrote:
 To avoid the delegate being GC allocated, use `scope foo = (int i) { ...
 }` at call site.
Is that true? At one point in D's past, this ONLY worked if you passed a delegate to a function accepting a scope delegate. Maybe it's been fixed. -Steve
I fixed that more than 4 years ago, https://github.com/dlang/dmd/pull/625.
Jun 11 2016
prev sibling parent reply maik klein <maikklein googlemail.com> writes:
On Thursday, 9 June 2016 at 22:06:24 UTC, Mathias Lang wrote:
 To avoid the delegate being GC allocated, use `scope foo = (int 
 i) { ... }`
 at call site.
 You can also make your function signature as `void func(scope 
 void
 delegate() dg)` in which case it won't allocate if you pass a 
 literal
 directly.

 2016-06-09 23:57 GMT+02:00 maik klein via Digitalmars-d < 
 digitalmars-d puremagic.com>:

 On Thursday, 9 June 2016 at 21:32:33 UTC, Alex Parrill wrote:

 On Thursday, 9 June 2016 at 21:02:26 UTC, maik klein wrote:

 Has this been done before?
Well, yes, the entire point of delegates is to be able to capture variables (as opposed to function pointers, which cannot). auto createADelegate(int captured) { return (int a) => captured + a; } void main() { auto dg1 = createADelegate(5); auto dg2 = createADelegate(32); assert(dg1(5) == 10); assert(dg1(10) == 15); assert(dg2(8) == 40); assert(dg2(32) == 64); } https://dpaste.dzfl.pl/90ebc29651f6 (Unfortunately template delegates, like the ones used with map, don't keep their captured variables alive after the captured variables go out of scope, but it doesn't sound like you need those)
I meant, "has this been implement as a library before". I am well aware that delegates exist in the language but as far as I know you can not do manual allocation with delegates (to avoid the GC).
But that means that the closure will be allocated on the stack right? What happens when I send it with http://dpldocs.info/experimental-docs/std.concurrency.send.html Will it copy the function or will it only send the pointer? Also scope on local vars is marked to be deprecated, see http://stackoverflow.com/a/4713064/944430 I don't think that I can use delegates (without the gc), what I basically do is send a delegate to a thread, create a fiber on that thread and put it in a thread local array. The delegate contains a "future" that I can access on a different thread. I use it as mechanism to share results. (Its synchronized with atomics) I mean currently I just use gc delegates, but I am exploring some alternatives.
Jun 09 2016
parent reply Mathias Lang via Digitalmars-d <digitalmars-d puremagic.com> writes:
2016-06-10 1:20 GMT+02:00 maik klein via Digitalmars-d <
digitalmars-d puremagic.com>:

 But that means that the closure will be allocated on the stack right? What
 happens when I send it with
 http://dpldocs.info/experimental-docs/std.concurrency.send.html

 Will it copy the function or will it only send the pointer?
Yes it will be stack allocated. But if you wish to `send` something, I think you're better off using a function in an object (disclaimer: never tried). Functions are not copied, so I guess you are refering to the context pointer ?
 Also scope on local vars is marked to be deprecated, see
 http://stackoverflow.com/a/4713064/944430
Yeah and they are not going away anytime soon. There's a reason why the compiler won't even warn about them. They are extremely useful and there is currently no replacement for them.
 I don't think that I can use delegates (without the gc), what I basically
 do is send a delegate to a thread, create a fiber on that thread and put it
 in a thread local array.
Did you try to send a native delegate ? I would be very surprised if you were allowed to do so.
 The delegate contains a "future" that I can access on a different thread.
 I use it as mechanism to share results. (Its synchronized with atomics)

 I mean currently I just use gc delegates, but I am exploring some
 alternatives.
Note that not all delegates allocate. Function local delegate refering to variable do, but the one refering to aggregate never do. E.g. the following compiles and run: ``` struct Foobar { string toString() nogc { return "Foobar"; } } void func (string delegate() ts) nogc {} void main () nogc { scope c = new Object; Foobar s; func(&c.toString); // ptr=c funcptr=toString func(&s.toString); //ptr =s funcptr=toString string delegate() nogc dg; dg.funcptr = (&s.toString).funcptr; // There might be a better way to do that dg.ptr = &s; assert("Foobar" == dg()); } ```
Jun 09 2016
parent maik klein <maikklein googlemail.com> writes:
On Friday, 10 June 2016 at 00:23:13 UTC, Mathias Lang wrote:
 2016-06-10 1:20 GMT+02:00 maik klein via Digitalmars-d < 
 digitalmars-d puremagic.com>:
 Did you try to send a native delegate ? I would be very 
 surprised if you were allowed to do so.
That is what I am currently doing and it at least appears to be working as intended.
 Note that not all delegates allocate. Function local delegate 
 refering to variable do, but the one refering to aggregate 
 never do.
All my closures/delegated need to capture a local variable. They capture a pointer to a heap allocated "Cell". I use the cell as a primitive to write my result into. It has an internal atomic counter, if the counter as decremented to 0, I know that I can safely read from it. I am not sure how I would implement it differently. So that means that all my delegates allocate, and they allocate with the GC.
Jun 11 2016
prev sibling parent reply BBasile <b2.temp gmx.com> writes:
On Thursday, 9 June 2016 at 21:02:26 UTC, maik klein wrote:
 I am currently writing a task system and I have this case where 
 I want to send a delegate to a different thread but this 
 delegate also captures a variable. I use this to implement a 
 "future".

 Now as far as I know this delegate will allocate GC memory and 
 I just wanted to avoid that, just for fun.

 Here is the code https://dpaste.dzfl.pl/cd77fce99a5b

 I have only worked on it a couple of hours and I am sure there 
 are many problems with it.

 Basically the idea is that you can use normal lambda syntax. If 
 you want a function that returns and int and takes an int, you 
 can write it like this:

 (int i) => i

 If you want a function that returns an int, takes an int, but 
 also captures and int you would write it like this

 (int i, int captured) => i + captured

 But you also have to declare the base function type without the 
 captured variables beforehand.

 Fn!(FnType!(int, int), (int i, int captured) => i + 
 captured)(42);

 That is how I know what the captured variables are.

 The only part that is currently missing are polymorphic 
 delegates. They are not too useful if I can't pack them into 
 the same array.

 I guess I have to do this with classes/interfaces.

 Thoughts?

 Has this been done before?
What you have here is more functor, to the extent that you can memorize the parameters. The problem is that it's not compatible with the delegates as defined in the language. Actual D delegates get collected when we take the address and the context of a member function with "&". To allocate the delegate itself in the non GC heap is easy (with a struct that contains two pointers). Your message has motivated me to make this, thanks to a union true nogc delegates are possible: ========================================= module runnable; union Delegate(FT) { // delegate layout as defined in the D ABI struct DgMembers { void* ptr; FT funcptr; } DgMembers members; // the 2nd member is "true" D delegate type import std.array: replace; mixin("alias DT = " ~ FT.stringof.replace("function", "delegate") ~ ";"); DT dg; } void main() nogc { static struct Foo { nogc string delegate() test; nogc string source() {return "test";} } import std.experimental.allocator.mallocator; import std.experimental.allocator; Foo foo; alias DelegateT = Delegate!(typeof(&Foo.source)); // &Foo.source takes the static address, i.e in the data segment DelegateT* dg = make!DelegateT(Mallocator.instance); dg.members.ptr = &foo; dg.members.funcptr = &Foo.source; foo.test = dg.dg; assert(foo.test() == "test"); } ========================================= Here you have a true D delegate that's conform with the language.
Jun 09 2016
parent maik klein <maikklein googlemail.com> writes:
On Thursday, 9 June 2016 at 23:53:33 UTC, BBasile wrote:
 On Thursday, 9 June 2016 at 21:02:26 UTC, maik klein wrote:
 I am currently writing a task system and I have this case 
 where I want to send a delegate to a different thread but this 
 delegate also captures a variable. I use this to implement a 
 "future".

 Now as far as I know this delegate will allocate GC memory and 
 I just wanted to avoid that, just for fun.

 Here is the code https://dpaste.dzfl.pl/cd77fce99a5b

 I have only worked on it a couple of hours and I am sure there 
 are many problems with it.

 Basically the idea is that you can use normal lambda syntax. 
 If you want a function that returns and int and takes an int, 
 you can write it like this:

 (int i) => i

 If you want a function that returns an int, takes an int, but 
 also captures and int you would write it like this

 (int i, int captured) => i + captured

 But you also have to declare the base function type without 
 the captured variables beforehand.

 Fn!(FnType!(int, int), (int i, int captured) => i + 
 captured)(42);

 That is how I know what the captured variables are.

 The only part that is currently missing are polymorphic 
 delegates. They are not too useful if I can't pack them into 
 the same array.

 I guess I have to do this with classes/interfaces.

 Thoughts?

 Has this been done before?
What you have here is more functor, to the extent that you can memorize the parameters. The problem is that it's not compatible with the delegates as defined in the language. Actual D delegates get collected when we take the address and the context of a member function with "&". To allocate the delegate itself in the non GC heap is easy (with a struct that contains two pointers). Your message has motivated me to make this, thanks to a union true nogc delegates are possible: ========================================= module runnable; union Delegate(FT) { // delegate layout as defined in the D ABI struct DgMembers { void* ptr; FT funcptr; } DgMembers members; // the 2nd member is "true" D delegate type import std.array: replace; mixin("alias DT = " ~ FT.stringof.replace("function", "delegate") ~ ";"); DT dg; } void main() nogc { static struct Foo { nogc string delegate() test; nogc string source() {return "test";} } import std.experimental.allocator.mallocator; import std.experimental.allocator; Foo foo; alias DelegateT = Delegate!(typeof(&Foo.source)); // &Foo.source takes the static address, i.e in the data segment DelegateT* dg = make!DelegateT(Mallocator.instance); dg.members.ptr = &foo; dg.members.funcptr = &Foo.source; foo.test = dg.dg; assert(foo.test() == "test"); } ========================================= Here you have a true D delegate that's conform with the language.
The problem is I currently do this struct LocalTaskQueue{ ... Array!(Box!Fiber) work; Array!(Box!Fiber) queuedWork; ... } This is where I submit my delegates to, but before that I also wrap them inside a fiber. Btw "Box" is my version of Unique. I execute a fiber inside work and if it is done I want to delete it, because it is not needed anymore. If it is on hold it transfer it from "work" to queuedWork. That means I need be able to put my delegates inside a fiber, this is the constructor for a Fiber currently. this( void delegate() dg, size_t sz = PAGESIZE*4 ) nothrow in { assert( dg ); } body { allocStack( sz ); reset( dg ); } I mean I can easily convert my "Fn" version to delegate with &foo.opCall, but then I would not know how long my "Fn" should stay alive. I think what I can do is switch my struct Fn to a class and implement my own function polymorphism. I would then do Array!(Box!Fiber) work; Array!(Box!PolymorphicFn!(void, void)) rawFunction; and when I delete a fiber from work I can delete it from rawFunction. But that seems to be so much trouble just to avoid a bit of GC.
Jun 11 2016