www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - search of a workaround

reply "Namespace" <rswhite4 googlemail.com> writes:
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.
Feb 08 2013
parent reply "monarch_dodra" <monarchdodra gmail.com> writes:
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.
Feb 08 2013
next sibling parent reply "Namespace" <rswhite4 googlemail.com> writes:
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?
Feb 08 2013
parent "Namespace" <rswhite4 googlemail.com> writes:
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
Feb 08 2013
prev sibling next sibling parent reply "Namespace" <rswhite4 googlemail.com> writes:
BTW: What's stopping you from using a class instead of a struct?
Feb 08 2013
parent reply "monarch_dodra" <monarchdodra gmail.com> writes:
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?
Feb 09 2013
parent reply "Namespace" <rswhite4 googlemail.com> writes:
 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?
Feb 09 2013
parent reply "monarch_dodra" <monarchdodra gmail.com> writes:
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.
Feb 09 2013
parent reply "Namespace" <rswhite4 googlemail.com> writes:
 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.
Feb 09 2013
parent reply "monarch_dodra" <monarchdodra gmail.com> writes:
On Saturday, 9 February 2013 at 12:10:57 UTC, Namespace wrote:

 In particular, you can't pack classes in a table, for example.
I don't understand.
I mean structs you can pack together in an array, eg: S[10] s10; //10 structs S You can't do that with classes. Sure, you can put 10 class *references* in an array, but the *instances* themselves need to be individually allocated. This can be a problem if you want to represent an image, for example, and "S" is an RGB quad.
Feb 09 2013
parent reply "bearophile" <bearophileHUGS lycos.com> writes:
monarch_dodra:

 I mean structs you can pack together in an array, eg:

 S[10] s10; //10 structs S

 You can't do that with classes. Sure, you can put 10 class 
 *references* in an array, but the *instances* themselves need 
 to be individually allocated.
This kind of works, with some limitations (I'll ask to remove one limitation): import std.stdio, std.conv, std.typecons; class Foo { int x; this(int x_) { this.x = x_; } override string toString() { return text(x); } } void main() { // scoped!Foo[10] foos; typeof(scoped!Foo(1))[10] foos; // Not initialized. foreach (i, ref f; foos) // f = new Foo(i * 10); // f = scoped!Foo(i * 10); f.x = i * 10; // writeln(foos); foreach (ref f; foos) writeln(f.x); } Bye, bearophile
Feb 09 2013
parent "bearophile" <bearophileHUGS lycos.com> writes:
 (I'll ask to remove one limitation):
http://d.puremagic.com/issues/show_bug.cgi?id=9489
     typeof(scoped!Foo(1))[10] foos; // Not initialized.

     foreach (i, ref f; foos)
         // f = new Foo(i * 10);
         // f = scoped!Foo(i * 10);
         f.x = i * 10;
What's the right/good way to initialize a scoped class instance? Currently this doesn't work: import std.typecons; class Foo { int x; this(int x_) { initialize(x_); } void initialize(int x_) { this.x = x_; } } void main() { typeof(scoped!Foo(1))[10] foos; foreach (i, ref f; foos) f.initialize(i * 10); } Bye, bearophile
Feb 09 2013
prev sibling next sibling parent Philippe Sigaud <philippe.sigaud gmail.com> writes:
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); }
Feb 08 2013
prev sibling parent reply "Namespace" <rswhite4 googlemail.com> writes:
Why isn't there 'const' in ParameterStorageClass?
How could I detect that my Parameter storage class is 'in'?
Feb 09 2013
parent reply "Namespace" <rswhite4 googlemail.com> writes:
It seems your template has problems with this:

struct A { }

class B {
public:
	const int bar(ref A a) {
		return 42;
	}
	
	mixin(rvalue!(bar));
}

remove the 'const' and it works fine.
Feb 09 2013
parent reply "Namespace" <rswhite4 googlemail.com> writes:
On Saturday, 9 February 2013 at 22:23:07 UTC, Namespace wrote:
 It seems your template has problems with this:

 struct A { }

 class B {
 public:
 	const int bar(ref A a) {
 		return 42;
 	}
 	
 	mixin(rvalue!(bar));
 }

 remove the 'const' and it works fine.
It seems that D has no functionality, to detect if a method is const.
Feb 09 2013
parent reply "Namespace" <rswhite4 googlemail.com> writes:
This works so far:

auto funcAttr = functionAttributes!(fun);
if (FunctionAttribute.pure_ & funcAttr) s ~= " pure";
if (FunctionAttribute.nothrow_ & funcAttr) s ~= " nothrow";
if (FunctionAttribute.ref_ & funcAttr) s ~= " ref";
if (!isMutable!(typeof(fun))) s ~= " const";

But it's curious that FunctionAttributes has no const, immutable, 
inout or shared...
Feb 09 2013
parent reply "monarch_dodra" <monarchdodra gmail.com> writes:
On Saturday, 9 February 2013 at 22:14:45 UTC, Namespace wrote:
 Why isn't there 'const' in ParameterStorageClass?
 How could I detect that my Parameter storage class is 'in'?
It's an ambiguity in the term "StorageClass" in D, which is different from "TypeQualifier". Const is part of the type. "StorageClass" means modifiers when you pass to a function. As for "in" (or inout): Apparently, they aren't "true" StorageClass, because the compiler re-writes them to "const ref" or "scope" something anyways, that's why they don't appear in the list. On Saturday, 9 February 2013 at 22:54:23 UTC, Namespace wrote:
 On Saturday, 9 February 2013 at 22:23:07 UTC, Namespace wrote:
 It seems your template has problems with this:

 struct A { }

 class B {
 public:
 	const int bar(ref A a) {
 		return 42;
 	}
 	
 	mixin(rvalue!(bar));
 }

 remove the 'const' and it works fine.
It seems that D has no functionality, to detect if a method is const.
Well, I did mention "Also, I didn't code the FunctionAttribute part, but that's just because it's late and I want to sleep". I even left a big "todo" in there :) On Sunday, 10 February 2013 at 00:01:32 UTC, Namespace wrote:
 This works so far:

 auto funcAttr = functionAttributes!(fun);
 if (FunctionAttribute.pure_ & funcAttr) s ~= " pure";
 if (FunctionAttribute.nothrow_ & funcAttr) s ~= " nothrow";
 if (FunctionAttribute.ref_ & funcAttr) s ~= " ref";
 if (!isMutable!(typeof(fun))) s ~= " const";

 But it's curious that FunctionAttributes has no const, 
 immutable, inout or shared...
I I have no idea. File a bug maybe? Either it'll be valid, or you'll get the "it would be redundant" reply, in which case the docs would *need* to be upgraded.
Feb 10 2013
next sibling parent reply "Namespace" <rswhite4 googlemail.com> writes:
 Well, I did mention "Also, I didn't code the FunctionAttribute 
 part, but that's just because it's late and I want to sleep". I 
 even left a big "todo" in there :)
I've overlooked that. :) Here is the complete code. If you have to improve something, say it please. One last question: why 'ss' as function name? :o [code] template rvalue(alias fun) { private string ss() { enum Func = __traits(identifier, fun); enum Ret = ReturnType!(fun).stringof; alias Args = ParameterTypeTuple!(fun); alias ParameterStorageClassTuple!(fun) pstc; enum names = [ParameterIdentifierTuple!(fun)]; string s; s ~= Ret ~ " " ~ Func ~ "("; 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 "; s ~= Args[i].stringof ~ " "; s ~= names[i]; if (i + 1 != Args.length) s ~= ", "; } s ~= ")"; auto funcAttr = functionAttributes!(fun); if (funcAttr & FunctionAttribute.pure_) s ~= " pure"; if (funcAttr & FunctionAttribute.nothrow_) s ~= " nothrow"; if (funcAttr & FunctionAttribute.ref_) s ~= " ref"; if (!isMutable!(typeof(fun))) s ~= " const"; // no idea why FunctionAttribute has no const... s ~= " {\n\treturn " ~ Func ~ "("; if (Args.length) { s ~= names[0]; foreach(i, Type; Args[1 .. $]) { s ~= ", " ~ names[i + 1]; } } s ~= ");\n}\n"; return s; } enum rvalue = ss(); } [/code]
Feb 10 2013
parent "Namespace" <rswhite4 googlemail.com> writes:
Improved version which matches immutable and const correct: 
http://dpaste.1azy.net/597affd2
Feb 10 2013
prev sibling parent reply "Namespace" <rswhite4 googlemail.com> writes:
On Sunday, 10 February 2013 at 08:38:23 UTC, monarch_dodra wrote:
 On Saturday, 9 February 2013 at 22:14:45 UTC, Namespace wrote:
 Why isn't there 'const' in ParameterStorageClass?
 How could I detect that my Parameter storage class is 'in'?
It's an ambiguity in the term "StorageClass" in D, which is different from "TypeQualifier". Const is part of the type. "StorageClass" means modifiers when you pass to a function.
Parameter storage classes are in, out, ref, lazy, const, immutable, shared, inout or scope. http://dlang.org/function.html
Feb 10 2013
parent "monarch_dodra" <monarchdodra gmail.com> writes:
On Sunday, 10 February 2013 at 14:41:04 UTC, Namespace wrote:
 On Sunday, 10 February 2013 at 08:38:23 UTC, monarch_dodra 
 wrote:
 On Saturday, 9 February 2013 at 22:14:45 UTC, Namespace wrote:
 Why isn't there 'const' in ParameterStorageClass?
 How could I detect that my Parameter storage class is 'in'?
It's an ambiguity in the term "StorageClass" in D, which is different from "TypeQualifier". Const is part of the type. "StorageClass" means modifiers when you pass to a function.
Parameter storage classes are in, out, ref, lazy, const, immutable, shared, inout or scope. http://dlang.org/function.html
Hum. The bug report is here: http://d.puremagic.com/issues/show_bug.cgi?id=8695 I got confused by Kenji's explanation. Still, I find it strange that "const, immutable and shared" are part of the storage class. I figured they were (they are) just part of the plain type.
Feb 10 2013