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);
}

Reply via email to