DIP25 draft available for destruction

deadalnix deadalnix at gmail.com
Thu Feb 7 23:06:34 PST 2013


On Friday, 8 February 2013 at 06:28:54 UTC, Chad Joan wrote:
> On 02/06/2013 02:38 AM, Andrei Alexandrescu wrote:
>> Probably it'll need a fair amount of tweaking. Anyhow it's in
>> destroyable form.
>>
>> http://wiki.dlang.org/DIP25
>>
>>
>> Thanks,
>>
>> Andrei
>
> A single delegate (closure) can be used to defer both reads and 
> writes on an arbitrary expression.
>
> This works on naked variables, references, pointers, 
> properties, and probably a number of other things I haven't 
> thought of yet.
>
> Here's the relevance to DIP25: closures already have 
> well-defined escape semantics.  The downside is that they 
> probably allocate heap way too aggressively in the current 
> implementation.  The upshot is that they are always 
> memory-safe.  This makes optimization a 
> quality-of-implementation issue: a sufficiently intelligent 
> compiler should be able to remove many allocations for 
> non-escaping closures.
>
> Perhaps ref parameters should be delegates under the hood 
> instead of pointers?
>
> The longterm disadvantage I see with this is the extra pointer 
> that must be passed/returned.  I wonder how hard it would be 
> for the compiler to instantiate specialized oldschool-pointer 
> versions of functions with ref parameters whenever calls are 
> made that do not require the guarantees that closures provide.
>
> A working demonstration is given below.
>
> Destroy!
>

The cost of passing a delegate is way higher than the cost of 
passing the extra pointer. Delegate cause an opaque function call 
so :
  - All registers that the callee is allowed to trash must be 
saved preventively (even if the callee don't trash them).
  - CPU cannot really use branch prediction.
  - The compiler must assume that the delegate call may have 
arbitrary side effects so have to commit everything to memory and 
then take everything back afterward. This prevent many 
instruction reordering, register promotions, dead read/write 
elimination, constant propagation and so on.

Considering passing by ref is sometime done to fasten things, 
this defeat the whole point.

Note that the operation above are not dying slow, but clearly not 
a good fit for ref.

>
> import std.traits;
> import std.stdio;
>
> struct Option(T)
> {
>     bool hasValue = false;
>     union
>     {
>         ubyte nope;
>         T value;
>     }
>
>     this( T value )
>     {
>         hasValue = true;
>         this.value = value;
>     }
> }
>
> Option!T none(T)()
> {
>     Option!T result;
>     return result;
> }
>
> /* For some reason we need to cast to this to make template 
> deduction work on any functions it gets passed into. */
> template DelegateRef(T)
> {
>     alias T delegate(Option!T intake) DelegateRef;
> }
>
> template isDelegateRef(T)
> {
>     static if ( isDelegate!T )
>     {
>         alias ReturnType!T R;
>         static if ( is( T == R delegate(Option!R) ) )
>             enum isDelegateRef = true;
>         else
>             enum isDelegateRef = false;
>     }
>     else
>         enum isDelegateRef = false;
> }
>
> string accessor(string expr)
> {
>     return
>         `cast(DelegateRef!(typeof(`~expr~`))) (`~
>         `delegate typeof(`~expr~`)(Option!(typeof(`~expr~`)) 
> intake) {`~
>         `    if ( intake.hasValue )`~
>         `        return (`~expr~` = intake.value);`~
>         `    else`~
>         `        return `~expr~`;`~
>         `})`;
> }
>
> // Function that accepts a reference.
> auto someFunc(Q)(Q qux) if ( isDelegateRef!Q )
> {
>     alias ReturnType!Q T;
>     auto x = qux(none!T);
>     x |= 0xF00D;
>     qux(Option!T(x));
>     return qux(none!T);
> }
>
> struct MyStruct
> {
>     private int m_q;
>
>     @property int q()
>     {
>         writefln("get q (%x)",m_q);
>         return m_q;
>     }
>
>     @property int q(int v)
>     {
>         writefln("set q (%x = %x)", m_q, v);
>         return m_q = v;
>     }
> }
>
> void testRef( ref int foo )
> {
>     assert(someFunc(mixin(accessor("foo"))) == 0xF00D);
> }
>
> void testPtr( int* foo )
> {
>     assert(someFunc(mixin(accessor("*foo"))) == 0xF00D);
> }
>
> void main()
> {
>     int abc = 0;
>     assert(someFunc(mixin(accessor("abc"))) == 0xF00D);
>     assert(abc == 0xF00D);
>
>     int foo = 0;
>     testRef(foo);
>     assert(foo == 0xF00D);
>
>     int bar = 0;
>     testPtr(&bar);
>     assert(bar == 0xF00D);
>
>     MyStruct s;
>     s.q = 0;
>     assert(someFunc(mixin(accessor("s.q"))) == 0xF00D);
>     assert(s.q == 0xF00D);
> }



More information about the Digitalmars-d mailing list