www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - re-creating C++'s reference bahavior

reply swiftcoder - Tristam MacDonald <swiftcoder darkcoda.com> writes:
I am fairly new to D, from a C++ background, and I am having a hard time coming
up with a sensible way to deal with what is a fairly trivial example in C++:

class Vector3f;

class Node
{
Vector3f &position();
};

Node n;

n.position().x = 0; // Works as expected, sets n.position().x to 0

Vector3f v = n.position();
v.x = 10; // Works as expected in C++ since v is a copy of n.position()

But now in D, I find that the last statement requires an explicit copy on the
user's part, otherwise they are using the actual instance, and may do nasty
things to it unintentionally. The obvious fix is to copy the vector in
Node.position(), but then you have lost the benefits of references, namely to
modify the variable. So it would seem that the only way to handle this is to
return a copy in the getter function, and  require explicit setting with the
setter method (thus losing constructs such as n.position().x = 0)?
Apr 16 2007
next sibling parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
swiftcoder - Tristam MacDonald wrote:
 I am fairly new to D, from a C++ background, and I am having a hard time
coming up with a sensible way to deal with what is a fairly trivial example in
C++:
 
 class Vector3f;
 
 class Node
 {
 Vector3f &position();
 };
 
 Node n;
 
 n.position().x = 0; // Works as expected, sets n.position().x to 0
 
 Vector3f v = n.position();
 v.x = 10; // Works as expected in C++ since v is a copy of n.position()
 
 But now in D, I find that the last statement requires an explicit copy on the
user's part, otherwise they are using the actual instance, and may do nasty
things to it unintentionally. The obvious fix is to copy the vector in
Node.position(), but then you have lost the benefits of references, namely to
modify the variable. So it would seem that the only way to handle this is to
return a copy in the getter function, and  require explicit setting with the
setter method (thus losing constructs such as n.position().x = 0)?

AFAIK, there's really no way to exactly duplicate C++'s references. However, if all "position()" is doing is returning a reference to a vector, then do you really need a function at all? If you just use a member variable, then the problem disappears. And incidentally, you can omit the parens with D since it's a function with no arguments. ie:
 auto v = n.position;
 v.x = 10;

("auto" is used here to trigger type inference -- if you specify a variable's storage class, you can omit the type itself.) -- Daniel -- int getRandomNumber() { return 4; // chosen by fair dice roll. // guaranteed to be random. } http://xkcd.com/ v2sw5+8Yhw5ln4+5pr6OFPma8u6+7Lw4Tm6+7l6+7D i28a2Xs3MSr2e4/6+7t4TNSMb6HTOp5en5g6RAHCP http://hackerkey.com/
Apr 16 2007
next sibling parent "akcom" <CppCoder gmail.com> writes:
 AFAIK, there's really no way to exactly duplicate C++'s references.
 However, if all "position()" is doing is returning a reference to a
 vector, then do you really need a function at all?  If you just use a
 member variable, then the problem disappears.

What about situations in which you're not just returning a reference to a class object? A templated hash map implementation for example, may want to do something like the D equivalent of template <class T, class K >T &HashMap<T,K>::find( K ) Things become a bit trickier if you're dealing with basic types. "Daniel Keep" <daniel.keep.lists gmail.com> wrote in message news:f0160f$bau$1 digitalmars.com...
 swiftcoder - Tristam MacDonald wrote:
 I am fairly new to D, from a C++ background, and I am having a hard time 
 coming up with a sensible way to deal with what is a fairly trivial 
 example in C++:

 class Vector3f;

 class Node
 {
 Vector3f &position();
 };

 Node n;

 n.position().x = 0; // Works as expected, sets n.position().x to 0

 Vector3f v = n.position();
 v.x = 10; // Works as expected in C++ since v is a copy of n.position()

 But now in D, I find that the last statement requires an explicit copy on 
 the user's part, otherwise they are using the actual instance, and may do 
 nasty things to it unintentionally. The obvious fix is to copy the vector 
 in Node.position(), but then you have lost the benefits of references, 
 namely to modify the variable. So it would seem that the only way to 
 handle this is to return a copy in the getter function, and  require 
 explicit setting with the setter method (thus losing constructs such as 
 n.position().x = 0)?

And incidentally, you can omit the parens with D since it's a function with no arguments. ie:
 auto v = n.position;
 v.x = 10;

("auto" is used here to trigger type inference -- if you specify a variable's storage class, you can omit the type itself.) -- Daniel -- int getRandomNumber() { return 4; // chosen by fair dice roll. // guaranteed to be random. } http://xkcd.com/ v2sw5+8Yhw5ln4+5pr6OFPma8u6+7Lw4Tm6+7l6+7D i28a2Xs3MSr2e4/6+7t4TNSMb6HTOp5en5g6RAHCP http://hackerkey.com/

Apr 16 2007
prev sibling parent swiftcoder <swiftcoder darkcoda.com> writes:
But this comes back to the basic problem:

Vector3f v = n.position;
v.x = 10; // changes n.position.x, since v is a reference to the same instance
as n.position is

Daniel Keep Wrote:
 
 
 AFAIK, there's really no way to exactly duplicate C++'s references.
 However, if all "position()" is doing is returning a reference to a
 vector, then do you really need a function at all?  If you just use a
 member variable, then the problem disappears.
 

Apr 17 2007
prev sibling next sibling parent reply Derek Parnell <derek nomail.afraid.org> writes:
On Mon, 16 Apr 2007 20:47:31 -0400, swiftcoder - Tristam MacDonald wrote:

 I am fairly new to D, from a C++ background, and I am having a
 hard time coming up with a sensible way to deal with what is
 a fairly trivial example in C++:
 ...
 But now in D, I find that the last statement requires ...

Does this below help any? //////////////////// import std.stdio; class Vector3f { int x; float y; } class Node { Vector3f m_position; // (reference to) a Vector Vector3f position() // Copy-getter { Vector3f t = new Vector3f; // copy fields by hand t.x = m_position.x; t.y = m_position.y; return t; } Vector3f* position_flds() // Reference-getter { return &m_position; } this() { m_position = new Vector3f; } } void main() { Node n = new Node; writefln("A %s %s", n.position.x, n.position.y); n.position_flds.x = 1; n.position_flds.y = 2.2; writefln("B %s %s", n.position.x, n.position.y); Vector3f v = n.position; writefln("C %s %s", v.x, v.y); v.x = 10; v.y = 9.9; writefln("D %s %s", v.x, v.y); writefln("E %s %s", n.position.x, n.position.y); } /////////////////// -- Derek (skype: derek.j.parnell) Melbourne, Australia "Justice for David Hicks!" 17/04/2007 5:30:04 PM
Apr 17 2007
parent reply swiftcoder <swiftcoder darkcoda.com> writes:
What it really gets down to, is that I don't want to increase the code
complexity needlessly. For many reasons, there shouldn't be a copy made when
all we want to do is query a member, and we musn't have a copy when we set a
member:

float x = n.position.x; // or:
n.position.x = 10;

But we do want a copy when we request the whole vector:

Vector3f v = n.position;

So maybe the best thing is to override opAssign() to copy (thus behaving like a
struct). Then the olnly thing to make sure is that functions taking a vector as
an 'in' argument make a local copy rather than modifying the argument directly.
And since opAdd, etc. already produce new vectors, I don't think that will be
much of a problem.

Does this make sense to you?

Derek Parnell Wrote:

 On Mon, 16 Apr 2007 20:47:31 -0400, swiftcoder - Tristam MacDonald wrote:
 
 I am fairly new to D, from a C++ background, and I am having a
 hard time coming up with a sensible way to deal with what is
 a fairly trivial example in C++:
 ...
 But now in D, I find that the last statement requires ...

Does this below help any? //////////////////// import std.stdio; class Vector3f { int x; float y; } class Node { Vector3f m_position; // (reference to) a Vector Vector3f position() // Copy-getter { Vector3f t = new Vector3f; // copy fields by hand t.x = m_position.x; t.y = m_position.y; return t; } Vector3f* position_flds() // Reference-getter { return &m_position; } this() { m_position = new Vector3f; } } void main() { Node n = new Node; writefln("A %s %s", n.position.x, n.position.y); n.position_flds.x = 1; n.position_flds.y = 2.2; writefln("B %s %s", n.position.x, n.position.y); Vector3f v = n.position; writefln("C %s %s", v.x, v.y); v.x = 10; v.y = 9.9; writefln("D %s %s", v.x, v.y); writefln("E %s %s", n.position.x, n.position.y); } /////////////////// -- Derek (skype: derek.j.parnell) Melbourne, Australia "Justice for David Hicks!" 17/04/2007 5:30:04 PM

Apr 17 2007
parent swiftcoder <swiftcoder darkcoda.com> writes:
Except of course that opAssign is not overload-able in this manner.

swiftcoder Wrote:

 What it really gets down to, is that I don't want to increase the code
complexity needlessly. For many reasons, there shouldn't be a copy made when
all we want to do is query a member, and we musn't have a copy when we set a
member:
 
 float x = n.position.x; // or:
 n.position.x = 10;
 
 But we do want a copy when we request the whole vector:
 
 Vector3f v = n.position;
 
 So maybe the best thing is to override opAssign() to copy (thus behaving like
a struct). Then the olnly thing to make sure is that functions taking a vector
as an 'in' argument make a local copy rather than modifying the argument
directly. And since opAdd, etc. already produce new vectors, I don't think that
will be much of a problem.
 
 Does this make sense to you?
 
 Derek Parnell Wrote:
 
 On Mon, 16 Apr 2007 20:47:31 -0400, swiftcoder - Tristam MacDonald wrote:
 
 I am fairly new to D, from a C++ background, and I am having a
 hard time coming up with a sensible way to deal with what is
 a fairly trivial example in C++:
 ...
 But now in D, I find that the last statement requires ...

Does this below help any? //////////////////// import std.stdio; class Vector3f { int x; float y; } class Node { Vector3f m_position; // (reference to) a Vector Vector3f position() // Copy-getter { Vector3f t = new Vector3f; // copy fields by hand t.x = m_position.x; t.y = m_position.y; return t; } Vector3f* position_flds() // Reference-getter { return &m_position; } this() { m_position = new Vector3f; } } void main() { Node n = new Node; writefln("A %s %s", n.position.x, n.position.y); n.position_flds.x = 1; n.position_flds.y = 2.2; writefln("B %s %s", n.position.x, n.position.y); Vector3f v = n.position; writefln("C %s %s", v.x, v.y); v.x = 10; v.y = 9.9; writefln("D %s %s", v.x, v.y); writefln("E %s %s", n.position.x, n.position.y); } /////////////////// -- Derek (skype: derek.j.parnell) Melbourne, Australia "Justice for David Hicks!" 17/04/2007 5:30:04 PM


Apr 17 2007
prev sibling next sibling parent BCS <ao pathlink.com> writes:
Reply to swiftcoder - Tristam MacDonald,

 I am fairly new to D, from a C++ background, and I am having a hard
 time coming up with a sensible way to deal with what is a fairly
 trivial example in C++:
 

 Vector3f v = n.position();

If I'm reading you correctly, then the problem is that in this line you want value semantics and are getting references semantics. One solution would be to make Vector3f a struct and have .position return a pointer to it. In D, pointers to structs and structs are almost identical from a syntax standpoint (no . vs ->) Then when you want value semantics you would use *n.position().
Apr 17 2007
prev sibling parent Henning Hasemann <hhasemann web.de> writes:
Maybe you want to take a look at this thread which
describes a fairly similar (if not the same) problem:

http://www.digitalmars.com/d/archives/digitalmars/D/Problem_with_Point_property_49990.html

Henning

-- 
v4sw7Yhw4ln0pr7Ock2/3ma7uLw5Xm0l6/7DGKi2e6t6ELNSTVXb7AHIMOen5a2Xs5Mr2g5ACPR
hackerkey.com
Apr 17 2007