www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Passing structs to functions

reply phant0m <asoeifjasoi sioudfhjsiuodhf.com> writes:
I came from a C++ world, so I'm used to passing structs by a 
const reference (I mean the case, where a function argument isn't 
changed by the function). C++ allows passing a temporary (rvalue) 
to a function, which accepts a const reference. D doesn't allow 
this. All I have found is a message from Andrei: 
http://digitalmars.com/d/archives/digitalmars/D/const_ref_rvalues
103509.html#N103514 Sadly, he didn't describe the details there. Let's suppose
he's right.
How should I pass a struct variable in D effectively? When 
passing by a const reference, I need to implement 2^N additional 
overloads, where N is the number of arguments. On the other hand, 
passing by value can be very ineffective.
Maybe I shouldn't bother at all and a compiler is smart enough to 
always optimize variables passed by value?
Please, share your best practices. There are many options here 
(immutable, const, ref, in, out, inout) and I don't know what I 
should prefer.
Jul 02 2016
next sibling parent reply ketmar <ketmar ketmar.no-ip.org> writes:
void boo() (in auto ref MyStruct s) { ... }

this will accept both lvalues and rvalues, and will avoid copying 
if it can.
Jul 02 2016
parent reply phant0m <asoeifjasoi sioudfhjsiuodhf.com> writes:
On Saturday, 2 July 2016 at 18:43:51 UTC, ketmar wrote:
 void boo() (in auto ref MyStruct s) { ... }

 this will accept both lvalues and rvalues, and will avoid 
 copying if it can.
Thank you! Could you please explain what does "auto" in this context mean?
Jul 02 2016
next sibling parent reply ketmar <ketmar ketmar.no-ip.org> writes:
On Saturday, 2 July 2016 at 18:47:31 UTC, phant0m wrote:
 On Saturday, 2 July 2016 at 18:43:51 UTC, ketmar wrote:
 void boo() (in auto ref MyStruct s) { ... }

 this will accept both lvalues and rvalues, and will avoid 
 copying if it can.
Thank you! Could you please explain what does "auto" in this context mean?
basically, what you think it does. ;-) it means "use ref if you can, but fallback to copy if you can't". so it accepts both: MyStruct s; boo(s); and boo(MyStruct()); note the first "()", though: this is effectively a template function, which compiler will instantiate either with "ref" or without it.
Jul 02 2016
parent reply phant0m <asoeifjasoi sioudfhjsiuodhf.com> writes:
On Saturday, 2 July 2016 at 19:25:37 UTC, ketmar wrote:
 note the first "()", though: this is effectively a template 
 function, which compiler will instantiate either with "ref" or 
 without it.
Yeah, I've noticed it. Always using function template for this use case seems like a weird idea.
Jul 02 2016
parent reply Namespace <rswhite4 gmail.com> writes:
On Saturday, 2 July 2016 at 19:40:53 UTC, phant0m wrote:
 On Saturday, 2 July 2016 at 19:25:37 UTC, ketmar wrote:
 note the first "()", though: this is effectively a template 
 function, which compiler will instantiate either with "ref" or 
 without it.
Yeah, I've noticed it. Always using function template for this use case seems like a weird idea.
Try this little trick: ---- import std.stdio; struct A { private A* _this = null; public int id = 0; this(int id) { this.id = id; } ref A byRef() { _this = &this; return *_this; } } void foo(ref const A a) { writeln(a.id); } void main() { foo(A(42).byRef()); } ----
Jul 02 2016
parent reply ketmar <ketmar ketmar.no-ip.org> writes:
On Saturday, 2 July 2016 at 21:05:18 UTC, Namespace wrote:
 Try this little trick:
or don't. such pointers to structs are *dangerous*.
Jul 02 2016
parent reply Namespace <rswhite4 gmail.com> writes:
On Saturday, 2 July 2016 at 21:15:29 UTC, ketmar wrote:
 On Saturday, 2 July 2016 at 21:05:18 UTC, Namespace wrote:
 Try this little trick:
or don't. such pointers to structs are *dangerous*.
Either that "dangerous" thing or 2^N template bloat.
Jul 02 2016
parent reply ketmar <ketmar ketmar.no-ip.org> writes:
On Saturday, 2 July 2016 at 21:17:33 UTC, Namespace wrote:
 On Saturday, 2 July 2016 at 21:15:29 UTC, ketmar wrote:
 On Saturday, 2 July 2016 at 21:05:18 UTC, Namespace wrote:
 Try this little trick:
or don't. such pointers to structs are *dangerous*.
Either that "dangerous" thing or 2^N template bloat.
not "dangerous", but *dangerous*.
Jul 02 2016
next sibling parent reply Namespace <rswhite4 gmail.com> writes:
On Saturday, 2 July 2016 at 21:19:04 UTC, ketmar wrote:
 On Saturday, 2 July 2016 at 21:17:33 UTC, Namespace wrote:
 On Saturday, 2 July 2016 at 21:15:29 UTC, ketmar wrote:
 On Saturday, 2 July 2016 at 21:05:18 UTC, Namespace wrote:
 Try this little trick:
or don't. such pointers to structs are *dangerous*.
Either that "dangerous" thing or 2^N template bloat.
not "dangerous", but *dangerous*.
I see no real danger in that code snippet of mine. 'auto ref' is the wrong solution since it leads to 2^N template bloat and passing by value is only a good solution if your struct is really small.
Jul 02 2016
parent Adam D. Ruppe <destructionator gmail.com> writes:
On Saturday, 2 July 2016 at 21:23:57 UTC, Namespace wrote:
 passing by value is only a good solution if your struct is 
 really small.
It's not uncommon for optimizers to generate the same code either way regardless of what you write.
Jul 02 2016
prev sibling parent Namespace <rswhite4 gmail.com> writes:
Just for you, a slightly adapted version:

----
import std.stdio;

struct A {
	public int id = 0;
	
	this(int id) {
		this.id = id;
	}
	
	ref A byRef() {
		return this;
	}
}

void foo(ref const A a) {
	writeln(a.id);
}

void main() {
	foo(A(42).byRef());
}
----
Jul 02 2016
prev sibling parent "H. S. Teoh via Digitalmars-d-learn" <digitalmars-d-learn puremagic.com> writes:
On Sat, Jul 02, 2016 at 06:47:31PM +0000, phant0m via Digitalmars-d-learn wrote:
 On Saturday, 2 July 2016 at 18:43:51 UTC, ketmar wrote:
 void boo() (in auto ref MyStruct s) { ... }
 
 this will accept both lvalues and rvalues, and will avoid copying if
 it can.
Thank you! Could you please explain what does "auto" in this context mean?
It's actually "auto ref", which roughly means "automatically decide whether it's more efficient to pass by value or pass by ref". The compiler will decide whether or not to pass by ref. T -- Life is too short to run proprietary software. -- Bdale Garbee
Jul 02 2016
prev sibling parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Saturday, 2 July 2016 at 18:37:06 UTC, phant0m wrote:
 How should I pass a struct variable in D effectively?
Passing by value is often the most efficient. It depends on what exactly you have in the struct.
Jul 02 2016
parent reply phant0m <asoeifjasoi sioudfhjsiuodhf.com> writes:
On Saturday, 2 July 2016 at 19:46:53 UTC, Adam D. Ruppe wrote:
 On Saturday, 2 July 2016 at 18:37:06 UTC, phant0m wrote:
 How should I pass a struct variable in D effectively?
Passing by value is often the most efficient. It depends on what exactly you have in the struct.
From the point of view of a hardcore C++ programmer, D looks very promising and very strange at the same time. I understand that my C++ habits are not applicable here, so I'm trying to find a "correct" way to do basic things. I took a simple task (for D learning purposes): to implement a Point template "class" (something like Qt's QPoint). As far as I want to be able to add two points, I've implemented opAdd() function: struct Point(T) { static assert(__traits(isArithmetic, T)); T x; T y; Point opAdd(const ref Point other) const { return Point(x + other.x, y + other.y); } } Now, to be able to use rvalues, I need to add another one: Point opAdd(const Point other) const { return opAdd(other); } That's where my question came from. Of course, I can use just one function, which accepts arguments by value. I'm just unsure whether it's a good decision and a common practice.
Jul 02 2016
parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Saturday, 2 July 2016 at 20:24:12 UTC, phant0m wrote:
 I took a simple task (for D learning purposes): to implement a 
 Point template "class" (something like Qt's QPoint).
Using ref is wasteful there regardless.... just take an ordinary Point (even const is optional if it is all value but it doesn't hurt). I think a lot of C++ programmers overuse references. If you're passing a large thing, it makes sense, but for small things it often has an overall negative effect on performance, but I see people saying they ALWAYS use struct& just out of habit. Even if you have an array slice or class object in there in D, they are already references, so you don't want to use ref again on top of it. Most the time, you're probably ok just passing a normal T or const T.
 As far as I want to be able to add two points, I've implemented 
  opAdd() function:
opAdd is the old way in D, nowadays it is more like Point opBinary(string op : "+")(const Point other) const { return Point(x + other.x, y + other.y); }
Jul 02 2016
parent ketmar <ketmar ketmar.no-ip.org> writes:
On Saturday, 2 July 2016 at 20:40:00 UTC, Adam D. Ruppe wrote:
 Using ref is wasteful there regardless.... just take an 
 ordinary Point (even const is optional if it is all value but 
 it doesn't hurt).

 I think a lot of C++ programmers overuse references. If you're 
 passing a large thing, it makes sense, but for small things it 
 often has an overall negative effect on performance, but I see 
 people saying they ALWAYS use struct& just out of habit.
yep. passing small structs is not really different from passing something like `(int x, int y)`. it is fast, and you spare yourself from one indirection, which is inevitable with `ref`.
Jul 02 2016