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!


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