Thread overview
re-creating C++'s reference bahavior
Apr 17, 2007
Daniel Keep
Apr 17, 2007
akcom
Apr 17, 2007
swiftcoder
Apr 17, 2007
Derek Parnell
Apr 17, 2007
swiftcoder
Apr 17, 2007
swiftcoder
Apr 17, 2007
BCS
Apr 17, 2007
Henning Hasemann
April 17, 2007
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)?
April 17, 2007

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/
April 17, 2007
> 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/


April 17, 2007
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
April 17, 2007
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

April 17, 2007
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().


April 17, 2007
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
> 

April 17, 2007
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
April 17, 2007
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.
>