Jump to page: 1 2 3
Thread overview
search of a workaround
Feb 08, 2013
Namespace
Feb 08, 2013
monarch_dodra
Feb 08, 2013
Namespace
Feb 09, 2013
Namespace
Feb 08, 2013
Namespace
Feb 09, 2013
monarch_dodra
Feb 09, 2013
Namespace
Feb 09, 2013
monarch_dodra
Feb 09, 2013
Namespace
Feb 09, 2013
monarch_dodra
Feb 09, 2013
bearophile
Feb 09, 2013
bearophile
Feb 09, 2013
Philippe Sigaud
Feb 09, 2013
Namespace
Feb 09, 2013
Namespace
Feb 09, 2013
Namespace
Feb 10, 2013
Namespace
Feb 10, 2013
monarch_dodra
Feb 10, 2013
Namespace
Feb 10, 2013
Namespace
Feb 10, 2013
Namespace
Feb 11, 2013
monarch_dodra
February 08, 2013
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.
February 08, 2013
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.
February 08, 2013
Thank you.
But it looks ugly, and I dislike the use of such constructs for such simple feature.
But cool thing. :D I like the meta stuff of D. :)

Other favorites?
February 08, 2013
BTW: What's stopping you from using a class instead of a struct?
February 09, 2013
On Friday, 8 February 2013 at 23:45:09 UTC, Namespace wrote:
> Thank you.
> But it looks ugly, and I dislike the use of such constructs for such simple feature.
> But cool thing. :D I like the meta stuff of D. :)
>
> Other favorites?

Currently I think that Solutions #2 or Solution #5 are the best solutions.
But I have no Idea which of them are more performant.
Solution #5 will cause many heap allocations and Solution #2 cause many many copies.
What would be the better choice?
I could of course wait for rvalue references. :D
February 09, 2013
On Sat, Feb 9, 2013 at 12:10 AM, monarch_dodra <monarchdodra@gmail.com> wrote:

> Here is a mixin template that *almost* does it:
(...)

> template rvalue(alias fun, string funs)
> {
>     private string ss()
>     {
>         //enum funs = fun.stringof; Don't know how to get function name :(

> 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).

Use __traits(identifier, foo) or std.traits.fullyQualifiedName:

module test;

import std.stdio;
import std.traits;

void foo() { writeln("called!");}

string nameOf(alias f)()
{
    return __traits(identifier, f);
}

void main()
{
    writeln(nameOf!foo);
    writeln(fullyQualifiedName!foo);
}
February 09, 2013
On Friday, 8 February 2013 at 23:45:09 UTC, Namespace wrote:
> Thank you.
> But it looks ugly, and I dislike the use of such constructs for such simple feature.
> But cool thing. :D I like the meta stuff of D. :)
>
> Other favorites?

One of the advantages of using this approach though is that it emulates auto ref. That means that if and when they get implemented for non-temlates, you'll need minimal code change to exploit it.

Also, instead of using the mixin, (indeed, kind of ugly), you could just print out the "rvalue!foo" in a unittest block, and copy paste it. It's boilerplate code, but copy paste is at least a bit safer than hand writen. And more convenient.

On Friday, 8 February 2013 at 23:49:01 UTC, Namespace wrote:
> BTW: What's stopping you from using a class instead of a struct?

I that directed at me? I'm not sure how it relates?
February 09, 2013
> I that directed at me? I'm not sure how it relates?

Yes, that's a question for you. Why would you take in spite of everything still structs rather than classes?
February 09, 2013
On Saturday, 9 February 2013 at 10:05:21 UTC, Namespace wrote:
>> I that directed at me? I'm not sure how it relates?
>
> Yes, that's a question for you. Why would you take in spite of everything still structs rather than classes?

I'm not sure what "in spite of" is. No offense, but you seem to be the only person having problems with this.

And the workarounds are plenty: My proposal, templatizing for auto ref, pass by value...

As for classes, they're fine if the object in question is somewhat big, but it's not a solution for tiny struct, such as color info or whatnot. In particular, you can't pack classes in a table, for example.

That said, if you pass everything by value, and make things < 16 bytes structs, and > 16 bytes classes, you should get a good deal.
February 09, 2013
> I'm not sure what "in spite of" is. No offense, but you seem to be the only person having problems with this.
Maybe I'm the only one who uses so many structs. That happens when you have strong C++ background. I miss '&'. :D

> And the workarounds are plenty: My proposal, templatizing for auto ref, pass by value...
I don't know. Templatizing just for auto ref seems a bad Idea for me.
But pass by value for small structs shouldn't be much overhead.

> In particular, you can't pack classes in a table, for example.
I don't understand.

> That said, if you pass everything by value, and make things < 16 bytes structs, and > 16 bytes classes, you should get a good deal.
Yes, that sounds good.
« First   ‹ Prev
1 2 3