On Friday, 8 February 2013 at 21:06:27 UTC, Namespace wrote:
I've been thinking about the lack of rvalue references. Therefore, I would like to know what you think is the best workaround for this feature. I illustrate below the only (*) five solutions briefly and summarized. What do you think is the best? Which of these do you use? Or did I forget some possible solutions? I'm looking for this for small data structures, such as vectors, matrices, or colors.

(*) In my opinion.

--------

struct A { }

 ---- Solution #1 ----

// rvalues
void foo(A a) {
        foo(a);
}

// lvalues
void foo(ref A a) {

}

----------------------------------
Summarized: flexible, efficient but code bloat, more effort and bug prone:

void foo(A a) {
        writeln("call no ref");
        foo(a);
}

void foo(const ref A a) {
        writeln("call ref");
}

void main() {
        foo(A());
}

-> endless loop!

 ---- Solution #2 ----

// fuck it, copy lvalues and rvalues
void foo(A a) {

}

----------------------------------
Summarized: flexible, no code bloat but inefficient -> unnecessary copies

 ---- Solution #3 ----

// allow only lvalues
void foo(ref A a) {

}

----------------------------------
Summarized: You have to make temporary values by yourself: unhandy, ugly and code bloat

 ---- Solution #4 ----

// accept only pointers
void foo(A* a) {

}

----------------------------------
Summarized: C Style, nullable and same problems as with solutions #3.

 ---- Solution #5 ----

// Use classes
class A { }

// Works for lvalues and rvalues
void foo(A a) {

}

----------------------------------
Summarized: flexible, efficient, no code bloat and same creation (with static opCall) as structs. But: heap allocations and nullable.

Honestly, I'd go with option 1. With a mixin template, it should be pretty to not mess up too hard.

Also, In regards to multi-args, I'd just bite the bullet, and make it so that if a single arg is rvalue, then all args are rvalue. Not perfect, but I think it is a good cost to gain ratio.

Here is a mixin template that *almost* does it:

//----
import std.stdio;
import std.conv;
import std.traits;

template rvalue(alias fun, string funs)
{
    private string ss()
    {
//enum funs = fun.stringof; Don't know how to get function name :(
        enum Ret  = ReturnType!fun.stringof;
        alias Args = ParameterTypeTuple!fun;
        alias ParameterStorageClassTuple!fun pstc;
        enum names = [ParameterIdentifierTuple!fun];

        string s;
        s ~= Ret ~ " " ~ funs ~ "(";
        foreach(i, Type; Args[0 .. $])
        {
if (pstc[i] == ParameterStorageClass.scope_) s ~= "scope "; if (pstc[i] == ParameterStorageClass.out_) s ~= "out "; if (pstc[i] == ParameterStorageClass.lazy_) s ~= "lazy "; //if (pstc[i] == ParameterStorageClass.ref_) s ~= "ref "; //Commented: That's the whole point ;)
            s ~= Args[i].stringof ~ " ";
            s ~= names[i];
            if (i + 1 != Args.length) s ~= ", ";
        }
        s ~= ")";
        //TODO: print the FunctionAttribute
        s ~= "\n{\n    return " ~ funs ~ "(";
        if (Args.length)
        {
            s ~= names[0];
            foreach(i, Type; Args[1 .. $])
            {
                s ~= ", " ~ names[i + 1];
            }
        }
        s ~= ");\n}\n";
        return s;
    }
    enum rvalue = ss();
}

void foo(ref int a, ref const int b, scope int c)
{
    writefln("%s %s %s", a, b, c);
}
mixin(rvalue!(foo, "foo"));

void main()
{
  foo(1, 2, 3);
  writeln(rvalue!(foo, "foo"));
}
//----
1 2 3
void foo(int a, const(int) b, scope int c)
{
    return foo(a, b, c);
}
//----

See? pretty nice. The only thing missing is actually getting the function name :/ I'm not sure how to do it, so I passed two arguments, but it should be doable with just 1 (worst case scenario, you pass just the string).

Also, I didn't code the FunctionAttribute part, but that's just because it's late and I want to sleep ;) It should be just about the same as with ParameterStorageClass.

You *could* further improve on this design, if you so felt like it, so that it generates all combination of ref/non ref parameters. That's more complicated. But doable.

Reply via email to