skinny delegates

Jonathan Marler johnnymarler at gmail.com
Thu Aug 2 16:37:29 UTC 2018


On Thursday, 2 August 2018 at 16:21:58 UTC, Jonathan Marler wrote:
> On Monday, 30 July 2018 at 21:02:56 UTC, Steven Schveighoffer 
> wrote:
>> Would it be a valid optimization to have D remove the 
>> requirement for allocation when it can determine that the 
>> entire data structure of the item in question is an rvalue, 
>> and would fit into the data pointer part of the delegate?
>>
>> Here's what I'm looking at:
>>
>> auto foo(int x)
>> {
>>    return { return x + 10; };
>> }
>>
>> In this case, D allocates a pointer on the heap to hold "x", 
>> and then return a delegate which uses the pointer to read x, 
>> and then return that plus 10.
>>
>> However, we could store x itself in the storage of the pointer 
>> of the delegate. This removes an indirection, and also saves 
>> the heap allocation.
>>
>> Think of it like "automatic functors".
>>
>> Does it make sense? Would it be feasible for the language to 
>> do this? The type system already casts the delegate pointer to 
>> a void *, so it can't make any assumptions, but this is a 
>> slight break of the type system.
>>
>> The two requirements I can think of are:
>> 1. The data in question must fit into a word
>> 2. It must be guaranteed that the data is not going to be 
>> mutated (either via the function or any other function). Maybe 
>> it's best to require the state to be const/immutable.
>>
>> I've had several cases where I was tempted to not use 
>> delegates because of the allocation cost, and simply return a 
>> specialized struct, but it's so annoying to do this compared 
>> to making a delegate. Plus something like this would be 
>> seamless with normal delegates as well (in case you do need a 
>> real delegate).
>>
>> -Steve
>
> I think the number of cases where you could optimize this is 
> very small.  And the complexity of getting the compiler to 
> analyze cases to determine when this is possible would be very 
> large.
>
> In addition, a developer can already do this explicitly if they 
> want, i.e.
>
> auto foo(int x)
> {
>     static struct DummyStructToMakeFunctionWithDelegateAbi
>     {
>         int passthru() const { return cast(int)&this; }
>     }
>     DummyStructToMakeFunctionWithDelegateAbi dummyStruct;
>     auto dg = &dummyStruct.passthru;
>     dg.ptr = cast(void*)(x + 10); // treat the void* pointer as 
> an int value
>     return dg;
> }
>
> void main(string[] args)
> {
>     auto dg = foo(32);
>     import std.stdio;
>     writefln("dg() = %s", dg());
> }
>
> It's definitely ugly but it works.  This will print the number 
> "42" as expected.
>
> This would be a case where DIP1011 extern(delegate) would come 
> in handy :) i.e.
>
> extern(delegate) int passthru(void* ptr) { return cast(int)ptr; 
> }
> int delegate() foo2(int x)
> {
>     return &(cast(void*)(x + 10)).passthru;
> }

Actually, I'll do you one better.  Here's a potential library 
function for it.  I'm calling these types of delegates "value 
pointer delegates".

// Assume this is in a library somewhere
auto makeValuePtrDelegate(string valueName, string funcBody, T)(T 
value)
{
     static struct DummyStruct
     {
         auto method() const
         {
             mixin("auto " ~ valueName ~ " = cast(T)&this;");
             mixin (funcBody);
         }
     }
     DummyStruct dummy;
     auto dg = &dummy.method;
     dg.ptr = cast(void*)value;
     return dg;
}

auto foo(int x)
{
     return makeValuePtrDelegate!("val", q{ return val + 10; })(x);
}

void main(string[] args)
{
     auto dg = foo(32);
     import std.stdio;
     writefln("dg() = %s", dg());
}



More information about the Digitalmars-d mailing list