July 12, 2015
On Sunday, 12 July 2015 at 08:38:01 UTC, Tofu Ninja wrote:
> Is it even possible?

Yes, though you need to use an entirely different approach for closures: make a struct.

Remember that delegates can come from local variable use or a `this` object being used and work the same way to the outside world. If it is local variables, the compiler emits the allocate calls and you have little control over it. If it comes from an object though, you have much more control: control over what is stored and control over how it is allocated.

The one notable thing you do NOT control is the delegate's lifetime. Once it becomes a delegate, it is interchangable with other delegates and is likely to be mixed in with other things and the recipient has no way of knowing where it came from. That's a feature in general, but it also means they can't know if they have to free() or release_reference() it.... if you need that, you might use a custom delegate type instead of the built in one.


Anyway though, just make a struct with the data you need to capture and a call method, then pass that around.


Before:

void foo() {
     int a;
     a = 10;
     // other code using a
     use_delegate( { a++; } );
}

After:

void foo() {
    static struct Captures {
      int a;
      void dg1() { a++; }
    }
    Captures captures;
    with(captures) {
       a = 10;
       // other code uing a
    }
    use_delegate(&captures.dg1);

    // WARNING: since captures is owned by this scope, use_delegate had better not hold on to it! But you could just as well have done Captures* captures = malloc(...) too.
}




So it is slightly more verbose and moves the inline delegate up to the struct (though if you got clever, you could probably change that too, like a delegate factory that takes a function and a this at the call point: dg( (_this) { _this.a++; }, captures); perhaps), but really the extra syntax overhead is small for real programs anyway.


And the clarity of what is and isn't captured might be worth it.
July 13, 2015
On Sun, 12 Jul 2015 12:45:10 +0000, Adam D. Ruppe wrote:

> On Sunday, 12 July 2015 at 11:42:09 UTC, ketmar wrote:
>> nope. there is no way to overload context allocation function,
>> afaik. at least without patching druntime, and i still don't know what
>> one have to patch. ;-)
> 
> _d_allocmemory

tnx. if i note that i don't know which function does something, there always be someone who will point me at the function. like it! ;-)

July 13, 2015
On Sunday, 12 July 2015 at 12:56:17 UTC, Adam D. Ruppe wrote:
> On Sunday, 12 July 2015 at 08:38:01 UTC, Tofu Ninja wrote:
>> Is it even possible?
>
> Yes, though you need to use an entirely different approach for closures: make a struct.
>
> [...]

This seems like a reasonable solution, even though it's not perfect, it seems like the only valid way to do this. Tnx :)

November 21, 2015
On Sunday, 12 July 2015 at 09:03:25 UTC, Tofu Ninja wrote:
> On Sunday, 12 July 2015 at 08:47:37 UTC, ketmar wrote:
>> On Sun, 12 Jul 2015 08:38:00 +0000, Tofu Ninja wrote:
>>
>>> Is it even possible?
>>
>> what do you mean?
>
> Sorry, thought the title was enough.
>
> The context for a delegate(assuming not a method delegate) is allocated by the GC. Is there any way to allocate the context manually.

Yes:

========================
class Foo
{
    void bar(){writeln(__PRETTY_FUNCTION__);}
}

auto uncollectedDelegate(T, string name)(ref T t)
{
    import std.experimental.allocator.mallocator;
    struct Dg{void* ptr, funcptr;}

    void* funcptr = &__traits(getMember, T, name);
    void* ptr = cast(void*)t;

    Dg* dg = cast(Dg*) Mallocator.instance.allocate(Dg.sizeof);
    dg.ptr = ptr;
    dg.funcptr = funcptr;
    return dg;
}

void main(string[] args)
{
    Foo foo = new Foo;
    auto dg = uncollectedDelegate!(Foo, "bar")(foo);
    auto tdg = cast(void delegate()*) dg;
    (*tdg)();
}
========================

with just a type __traits(getMember,...) on a delegate will only return the function address in the process image. so without the context ptr, just like a static or a global function. Later you set the context by hand, using a pointer to an instance.


November 21, 2015
On Saturday, 21 November 2015 at 00:30:45 UTC, userABCabc123 wrote:
> 
> Yes:
>
> ========================
> class Foo
> {
>     void bar(){writeln(__PRETTY_FUNCTION__);}
> }
>
> auto uncollectedDelegate(T, string name)(ref T t)
> {
>     import std.experimental.allocator.mallocator;
>     struct Dg{void* ptr, funcptr;}
>
>     void* funcptr = &__traits(getMember, T, name);
>     void* ptr = cast(void*)t;
>
>     Dg* dg = cast(Dg*) Mallocator.instance.allocate(Dg.sizeof);
>     dg.ptr = ptr;
>     dg.funcptr = funcptr;
>     return dg;
> }
>
> void main(string[] args)
> {
>     Foo foo = new Foo;
>     auto dg = uncollectedDelegate!(Foo, "bar")(foo);
>     auto tdg = cast(void delegate()*) dg;
>     (*tdg)();
> }
> ========================
>
> with just a type __traits(getMember,...) on a delegate will only return the function address in the process image. so without the context ptr, just like a static or a global function. Later you set the context by hand, using a pointer to an instance.

I think you are misunderstanding a bit what my original question was. The delegate it self is not gc allocated. The delegate it self is just 2 pointers(a function pointer and a context pointer), they are simply stack allocated. I am not woried about allocating that bit. What I am worried about allocating is the context, and primarily the context for nested functions(often called a closure).

For delegates to member functions, the context pointer is always just a pointer to the object that you pulled the delegate off of. To avoid gc allocating the context there, all you need to do is just don't gc allocate the object. Simple. In your example the context is still gc allocated with new Foo;. You actually added in another allocation on top of that with the mallocator. You now allocate the delegate it self, which normally can just be stack allocated.

The problem that I was concerned about was the case of nested function delegates. For delegates to nested functions that need to generate a closure, the allocation of the closure is implicitly done and done with the gc. As far as I can tell, there is no way to know anything about that closure, not its size, layout, contents, no way to manually allocate it.

Example:
auto foo(int x)
{
   int bar(){ return x; }
   return &bar; // <-- implicitly allocates a closure with the gc
}
November 21, 2015
On Sunday, 12 July 2015 at 08:38:01 UTC, Tofu Ninja wrote:
> Is it even possible?

You can use function instead delegate, and bind captured variables as struct:

http://dpaste.dzfl.pl/6e23bbcfe17f

auto bind(F: R function(ARGS), R, ARGS...)(F fn, ARGS args) @nogc {
	struct Functor {
		F fn;
		ARGS args;
		this(F fn_, ARGS args_) {
			fn = fn_;
			args = args_;
		}
		R opCall() { return fn(args); }
	}
	return Functor(fn, args);
}

import std.stdio;

auto foo(int x) {
   static int bar(ref int x){ return x++; }
   return bind(&bar, x);
}

void main() {
	auto f1 = foo(5);
	auto f2 = foo(10);
	writefln("%s, %s, %s", f1(), f1(), f1());
	writefln("%s, %s, %s", f2(), f2(), f2());
}

1 2
Next ›   Last »