www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Alternative solutions to returning ref types.

reply kede <dev null.com> writes:
Hi...

I'm still having a great time using D for graphics programming at the moment.
However, I'm constantly irritated by my initial design decision to implement
the lower level objects as structs simply because it has led do (a now
ridiculous amount) of code along the lines of the following:

model.origin = model.origin + vec3(0,1,0);
 -- or --
o = model.origin;
o.y += 1;
model.orgin = o;

When all I really want is just:   model.origin.y++;
Worse still, model.origin.y++ is legal but doesn't do what you would expect.

I'm sure most of you have this problem to some degree or another.  However, it
doesn't look like returnable ref types are going to be implemented in D any
time soon, so I want to ask you guys what you are currently doing to deal with
this.

For the sake of discussion take a 3d vector object as an example.
Given that it is small in size, frequently allocated and deallocated, commonly
used inside other class objects.

The obvious solutions are:
1. Use a pointer to return it.  -- urgh
2. Implement it as a class.
3. As 2 with custom 'new' 'delete'


Thanks in advance for any ideas you might have...

Have fun,
k
Mar 15 2008
next sibling parent Bill Baxter <dnewsgroup billbaxter.com> writes:
kede wrote:
 Hi...
 
 I'm still having a great time using D for graphics programming at the moment.
 However, I'm constantly irritated by my initial design decision to implement
the lower level objects as structs simply because it has led do (a now
ridiculous amount) of code along the lines of the following:
 
 model.origin = model.origin + vec3(0,1,0);
  -- or --
 o = model.origin;
 o.y += 1;
 model.orgin = o;
I take it model.origin is a get/set property method pair? If in C++ you would be willing to make it a reference-returning method then you might just want to make it a regular data member. Returning references basically means giving up most of your encapsulation benefit in the first place. And since methods and members can be accessed with the same syntax in D you can just change it back to property methods later if the need for encapsulation arises.
 When all I really want is just:   model.origin.y++;
 Worse still, model.origin.y++ is legal but doesn't do what you would expect.
You can define a static data member vec3.UnitY. Then if origin is a plain data member then this works: model.origin += vec3.UnitY;
 
 I'm sure most of you have this problem to some degree or another.  However, it
doesn't look like returnable ref types are going to be implemented in D any
time soon, so I want to ask you guys what you are currently doing to deal with
this.
It is annoying, yes.
 For the sake of discussion take a 3d vector object as an example.
 Given that it is small in size, frequently allocated and deallocated, commonly
used inside other class objects.
 
 The obvious solutions are:
 1. Use a pointer to return it.  -- urgh
I often end up having a regular .thing() method for typical uses, and in addition a .thing_ptr() method that returns a pointer for the cases where you really need to get an lvalue.
 2. Implement it as a class.
 3. As 2 with custom 'new' 'delete'
 
 
 Thanks in advance for any ideas you might have...
It is definitely annoying. The idea of gating all reads and writes through property methods is nice, but if that's how D wants us to do it, then the compiler really should rewrite expressions like a.foo += bar for us (as a.foo = a.foo+bar). This has been suggested several times in was the one brought up). I guess it just isn't a priority with Walter. It's a shame because it seems like it would be pretty easy to put those rewrite rules into place. But maybe its harder than I'm thinking. --bb
Mar 15 2008
prev sibling next sibling parent reply "Simen Kjaeraas" <simen.kjaras gmail.com> writes:
On Sat, 15 Mar 2008 10:38:52 +0100, kede <dev null.com> wrote:

 Hi...

 I'm still having a great time using D for graphics programming at the =
=
 moment.
 However, I'm constantly irritated by my initial design decision to  =
 implement the lower level objects as structs simply because it has led=
=
 do (a now ridiculous amount) of code along the lines of the following:=
 model.origin =3D model.origin + vec3(0,1,0);
  -- or --
 o =3D model.origin;
 o.y +=3D 1;
 model.orgin =3D o;

 When all I really want is just:   model.origin.y++;
 Worse still, model.origin.y++ is legal but doesn't do what you would  =
 expect.

 I'm sure most of you have this problem to some degree or another.   =
 However, it doesn't look like returnable ref types are going to be  =
 implemented in D any time soon, so I want to ask you guys what you are=
=
 currently doing to deal with this.

 For the sake of discussion take a 3d vector object as an example.
 Given that it is small in size, frequently allocated and deallocated, =
=
 commonly used inside other class objects.

 The obvious solutions are:
 1. Use a pointer to return it.  -- urgh
 2. Implement it as a class.
 3. As 2 with custom 'new' 'delete'


 Thanks in advance for any ideas you might have...

 Have fun,
 k
Just for the fun of it, I decided to try make a property struct, with = overloaded operators. Any ideas for how to make this better are welcome.= Of course, this causes some overhead, but was a fun project, and if it = helps anyone, I'm a happy cat. Example usage: class foo { private: int _a; float _b; public: property!(int) a; property!(float) b; = this() { a =3D &_a; b =3D &_b; } } -- Simen ----------------------------------- struct property(T) { T* data; = T opNeg() { return -(*data); } = T opPos() { return +(*data); } = T opPostInc() { return (*data)++; } = T opPostDec() { return (*data)--; } = const { T opAdd(U)(U rhs) { return (*data) + rhs; } = T opAdd_r(U)(U rhs) { return rhs + (*data); } = T opSub(U)(U rhs) { return (*data) - rhs; } = T opSub_r(U)(U rhs) { return rhs - (*data); } = T opMul(U)(U rhs) { return (*data) * rhs; } = T opMul_r(U)(U rhs) { return rhs * (*data); } = T opDiv(U)(U rhs) { return (*data) / rhs; } = T opDiv_r(U)(U rhs) { return rhs / (*data); } = T opMod(U)(U rhs) { return (*data) % rhs; } = T opMod_r(U)(U rhs) { return rhs % (*data); } = T opAnd(U)(U rhs) { return (*data) & rhs; } = T opAnd_r(U)(U rhs) { return rhs & (*data); } = T opOr(U)(U rhs) { return (*data) | rhs; } = T opOr(U)(U rhs) { return rhs | (*data); } = T opXor(U)(U rhs) { return (*data) ^ rhs; } = T opXor_r(U)(U rhs) { return rhs ^ (*data); } = T opShl(U)(U rhs) { return (*data) << rhs; } = T opShl_r(U)(U rhs) { return rhs << (*data); } = T opShr(U)(U rhs) { return (*data) >> rhs; } = T opShr_r(U)(U rhs) { return rhs >> (*data); } = T opUShr(U)(U rhs) { return (*data) >>> rhs; } = T opUShrs(U)(U rhs) { return rhs >>> (*data); } = T opCat(U)(U rhs) { return (*data) ~ rhs; } = T opCat_r(U)(U rhs) { return rhs ~ (*data); } = } = T opAssign(U)(U rhs) { static if (is (U : typeof(*this))) { data =3D rhs.data; } else static if (is(U : T*)) { data =3D rhs; } else { (*data) =3D rhs; } return *data; } = T opAddAssign(U)(U rhs) { *data +=3D rhs; return *data; } = T opSubAssign(U)(U rhs) { *data -=3D rhs; return *data; } = T opMulAssign(U)(U rhs) { *data *=3D rhs; return *data; } = T opDivAssign(U)(U rhs) { *data /=3D rhs; return *data; } = T opModAssign(U)(U rhs) { *data %=3D rhs; return *data; } = T opAndAssign(U)(U rhs) { *data &=3D rhs; return *data; } = T opOrAssign(U)(U rhs) { *data |=3D rhs; return *data; } = T opXorAssign(U)(U rhs) { *data ^=3D rhs; return *data; } = T opShlAssign(U)(U rhs) { *data <<=3D rhs; return *data; } = T opShrAssign(U)(U rhs) { *data >>=3D rhs; return *data; } = T opUShrAssign(U)(U rhs) { *data >>>=3D rhs; return *data; } } property!(T) Property(T)(T* _data) { property!(T) tmp; tmp.data =3D _data; return tmp; }
Mar 17 2008
parent Christopher Wright <dhasenan gmail.com> writes:
Simen Kjaeraas wrote:
 Just for the fun of it, I decided to try make a property struct, with 
 overloaded operators. Any ideas for how to make this better are welcome.
 Of course, this causes some overhead, but was a fun project, and if it 
 helps anyone, I'm a happy cat.
 
 
 Example usage:
 
 class foo
 {
 private:
     int _a;
     float _b;
 public:
     property!(int) a;
     property!(float) b;
     
     this()
     {
         a = &_a;
         b = &_b;
     }
 }
 
 -- Simen
Have you tried it with any UDTs that don't overload all those operators? The unary operators will die; the binary ones are hidden behind templates so they're okay unless someone uses them. You'll need guards like: T opNeg() { static if (is (typeof(-(*data)))) { return -(*data); } else { assert (false, "type " ~ T.stringof ~ " does not support this operator." } }
Mar 17 2008
prev sibling parent Simen Kjaeraas <simen.kjaras gmail.com> writes:
Christopher Wright Wrote:
 Have you tried it with any UDTs that don't overload all those operators? 
 The unary operators will die; the binary ones are hidden behind 
 templates so they're okay unless someone uses them.
 
 You'll need guards like:
 T opNeg()
 {
 	static if (is (typeof(-(*data))))
 	{
 		return -(*data);
 	}
 	else
 	{
 		assert (false, "type " ~ T.stringof ~ " does not support this operator."
 	}
 }
Right. Tried it before I added the unary operators, forgot to test those. Thanks. -- Simen
Mar 17 2008