www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - "inout return type" and "inout variable"

reply Hong Wing <Hong_member pathlink.com> writes:
Thanks everyone here for your great work in D, especially Walter who makes it
all happen.
Here I am requesting a feature, which is "inout variable" and "inout return
type"
Rationale, for the example below,

struct Foo
{
int x, int y, int z;
}

class Bar
{
private:
Foo m_foo;
public:
Foo foo() { return m_foo; }
Foo foo(Foo value) { return m_foo = value; }
}

now if i want to change the value of m_foo.x to 5, i have to do

Foo foo = bar.foo;
foo.x   = 5;
bar.foo = foo;

it would be nice if I can define a return type as "inout Foo"
inout Foo foo() { return m_foo;}

To extend it further, one might want to define inout variables as
inout Foo foo = bar.foo;

Advantages:

1. efficiency, one can directly change the states of the struct instead of
making a copy first, change the copy, and put the result back to the struct

2. less buggy, having to type 3 lines always increases chance of bugs, also,
someone might inadvertantly do bar.foo.x = 5 and forgot that he is working on a
copy

3. reduce the use of pointers, the above can be achieved using pointers, but it
means defining two versions of the same method every time a struct property is
written.
Mar 15 2006
next sibling parent Ben Phillips <Ben_member pathlink.com> writes:
In article <dvb0tq$1ptf$1 digitaldaemon.com>, Hong Wing says...
Thanks everyone here for your great work in D, especially Walter who makes it
all happen.
Here I am requesting a feature, which is "inout variable" and "inout return
type"
Rationale, for the example below,

struct Foo
{
int x, int y, int z;
}

class Bar
{
private:
Foo m_foo;
public:
Foo foo() { return m_foo; }
Foo foo(Foo value) { return m_foo = value; }
}

now if i want to change the value of m_foo.x to 5, i have to do

Foo foo = bar.foo;
foo.x   = 5;
bar.foo = foo;

it would be nice if I can define a return type as "inout Foo"
inout Foo foo() { return m_foo;}

To extend it further, one might want to define inout variables as
inout Foo foo = bar.foo;

Advantages:

1. efficiency, one can directly change the states of the struct instead of
making a copy first, change the copy, and put the result back to the struct

2. less buggy, having to type 3 lines always increases chance of bugs, also,
someone might inadvertantly do bar.foo.x = 5 and forgot that he is working on a
copy

3. reduce the use of pointers, the above can be achieved using pointers, but it
means defining two versions of the same method every time a struct property is
written.

This is a good suggestion imho and is probably fairly easy for Walter to implement as well.
Mar 16 2006
prev sibling parent reply "Regan Heath" <regan netwin.co.nz> writes:
On Thu, 16 Mar 2006 06:34:34 +0000 (UTC), Hong Wing  
<Hong_member pathlink.com> wrote:
 Thanks everyone here for your great work in D, especially Walter who  
 makes it
 all happen.
 Here I am requesting a feature, which is "inout variable" and "inout  
 return
 type"
 Rationale, for the example below,

 struct Foo
 {
 int x, int y, int z;
 }

 class Bar
 {
 private:
 Foo m_foo;
 public:
 Foo foo() { return m_foo; }
 Foo foo(Foo value) { return m_foo = value; }
 }

 now if i want to change the value of m_foo.x to 5, i have to do

 Foo foo = bar.foo;
 foo.x   = 5;
 bar.foo = foo;

 it would be nice if I can define a return type as "inout Foo"
 inout Foo foo() { return m_foo;}

 To extend it further, one might want to define inout variables as
 inout Foo foo = bar.foo;

 Advantages:

 1. efficiency, one can directly change the states of the struct instead  
 of
 making a copy first, change the copy, and put the result back to the  
 struct

 2. less buggy, having to type 3 lines always increases chance of bugs,  
 also,
 someone might inadvertantly do bar.foo.x = 5 and forgot that he is  
 working on a
 copy

 3. reduce the use of pointers, the above can be achieved using pointers,  
 but it
 means defining two versions of the same method every time a struct  
 property is
 written.

You're essentially asking for the ability to have variables which are references to structs (at least during assignment) and to return references to structs, all without using the pointer syntax. I think these things are un-necessary, and I think what you want can be achieved with pointers, without the penalty you mention in #3 above, see: import std.string; import std.stdio; struct Foo { int x; int y; int z; char[] toString() { return "("~std.string.toString(x)~","~std.string.toString(y)~","~std.str ng.toString(z)~")"; } } class Bar { private: Foo m_foo; public: Foo* foo() { return &m_foo; } Foo* foo(Foo* value) { m_foo = *value; return foo(); } Foo* foo(Foo value) { m_foo = value; return foo(); } } void main() { Bar b = new Bar(); Bar c = new Bar(); Foo t; b.foo.x = 1; b.foo.y = 2; b.foo.z = 3; writefln(b.foo.toString()); c.foo = b.foo; writefln(c.foo.toString()); t.x = 4; t.y = 5; t.z = 6; writefln(t.toString()); b.foo = t; writefln(b.foo.toString()); } Note: The explicit "toString" calls are required because otherwise it prints the pointer address. Regan
Mar 16 2006
parent reply Hong Wing <Hong_member pathlink.com> writes:
There are 3 drawbacks with the pointer approach

1. Noticed that you have 2 versions of the same method, one takes Foo in by
pointer and one takes in by value.
2. returning pointer would break client code if a field is changed to a
property.
3. you cannot call operators from pointers, without adding in * everywhere.

In article <ops6i24bii23k2f5 nrage.netwin.co.nz>, Regan Heath says...
On Thu, 16 Mar 2006 06:34:34 +0000 (UTC), Hong Wing  
<Hong_member pathlink.com> wrote:
 Thanks everyone here for your great work in D, especially Walter who  
 makes it
 all happen.
 Here I am requesting a feature, which is "inout variable" and "inout  
 return
 type"
 Rationale, for the example below,

 struct Foo
 {
 int x, int y, int z;
 }

 class Bar
 {
 private:
 Foo m_foo;
 public:
 Foo foo() { return m_foo; }
 Foo foo(Foo value) { return m_foo = value; }
 }

 now if i want to change the value of m_foo.x to 5, i have to do

 Foo foo = bar.foo;
 foo.x   = 5;
 bar.foo = foo;

 it would be nice if I can define a return type as "inout Foo"
 inout Foo foo() { return m_foo;}

 To extend it further, one might want to define inout variables as
 inout Foo foo = bar.foo;

 Advantages:

 1. efficiency, one can directly change the states of the struct instead  
 of
 making a copy first, change the copy, and put the result back to the  
 struct

 2. less buggy, having to type 3 lines always increases chance of bugs,  
 also,
 someone might inadvertantly do bar.foo.x = 5 and forgot that he is  
 working on a
 copy

 3. reduce the use of pointers, the above can be achieved using pointers,  
 but it
 means defining two versions of the same method every time a struct  
 property is
 written.

You're essentially asking for the ability to have variables which are references to structs (at least during assignment) and to return references to structs, all without using the pointer syntax. I think these things are un-necessary, and I think what you want can be achieved with pointers, without the penalty you mention in #3 above, see: import std.string; import std.stdio; struct Foo { int x; int y; int z; char[] toString() { return "("~std.string.toString(x)~","~std.string.toString(y)~","~std.str ng.toString(z)~")"; } } class Bar { private: Foo m_foo; public: Foo* foo() { return &m_foo; } Foo* foo(Foo* value) { m_foo = *value; return foo(); } Foo* foo(Foo value) { m_foo = value; return foo(); } } void main() { Bar b = new Bar(); Bar c = new Bar(); Foo t; b.foo.x = 1; b.foo.y = 2; b.foo.z = 3; writefln(b.foo.toString()); c.foo = b.foo; writefln(c.foo.toString()); t.x = 4; t.y = 5; t.z = 6; writefln(t.toString()); b.foo = t; writefln(b.foo.toString()); } Note: The explicit "toString" calls are required because otherwise it prints the pointer address. Regan

Mar 16 2006
parent reply "Regan Heath" <regan netwin.co.nz> writes:
On Fri, 17 Mar 2006 06:17:41 +0000 (UTC), Hong Wing  
<Hong_member pathlink.com> wrote:
 There are 3 drawbacks with the pointer approach

 1. Noticed that you have 2 versions of the same method, one takes Foo in  
 by pointer and one takes in by value.

That is an option, not a drawback. It gives you the _option_ of passing either a pointer to a Foo or a Foo directly. It's more than you could do previously :)
 2. returning pointer would break client code if a field is changed to a
 property.

How? Can you give me an example of some code, written to use a field, which fails when it becomes a property? (assuming of course that the required operator overloads are added, like the one I show below) If I add: public Foo ffoo; to Bar, I can say: b.ffoo = t; b.ffoo.x = 5; which looks the same as: b.foo = t; b.foo.x = 5;
 3. you cannot call operators from pointers, without adding in *  
 everywhere.

Partially true, i.e. when you're trying to add two Foo* together, however you can add the following to the Foo struct to add a Foo* to a Foo: Foo opAdd(Foo rhs) { Foo n; n.x = x+rhs.x; n.y = y+rhs.y; n.z = z+rhs.z; } Foo opAdd(Foo* rhs) { return opAdd(*rhs); } Regan
 In article <ops6i24bii23k2f5 nrage.netwin.co.nz>, Regan Heath says...
 On Thu, 16 Mar 2006 06:34:34 +0000 (UTC), Hong Wing
 <Hong_member pathlink.com> wrote:
 Thanks everyone here for your great work in D, especially Walter who
 makes it
 all happen.
 Here I am requesting a feature, which is "inout variable" and "inout
 return
 type"
 Rationale, for the example below,

 struct Foo
 {
 int x, int y, int z;
 }

 class Bar
 {
 private:
 Foo m_foo;
 public:
 Foo foo() { return m_foo; }
 Foo foo(Foo value) { return m_foo = value; }
 }

 now if i want to change the value of m_foo.x to 5, i have to do

 Foo foo = bar.foo;
 foo.x   = 5;
 bar.foo = foo;

 it would be nice if I can define a return type as "inout Foo"
 inout Foo foo() { return m_foo;}

 To extend it further, one might want to define inout variables as
 inout Foo foo = bar.foo;

 Advantages:

 1. efficiency, one can directly change the states of the struct instead
 of
 making a copy first, change the copy, and put the result back to the
 struct

 2. less buggy, having to type 3 lines always increases chance of bugs,
 also,
 someone might inadvertantly do bar.foo.x = 5 and forgot that he is
 working on a
 copy

 3. reduce the use of pointers, the above can be achieved using  
 pointers,
 but it
 means defining two versions of the same method every time a struct
 property is
 written.

You're essentially asking for the ability to have variables which are references to structs (at least during assignment) and to return references to structs, all without using the pointer syntax. I think these things are un-necessary, and I think what you want can be achieved with pointers, without the penalty you mention in #3 above, see: import std.string; import std.stdio; struct Foo { int x; int y; int z; char[] toString() { return "("~std.string.toString(x)~","~std.string.toString(y)~","~std.string.toString(z)~")"; } } class Bar { private: Foo m_foo; public: Foo* foo() { return &m_foo; } Foo* foo(Foo* value) { m_foo = *value; return foo(); } Foo* foo(Foo value) { m_foo = value; return foo(); } } void main() { Bar b = new Bar(); Bar c = new Bar(); Foo t; b.foo.x = 1; b.foo.y = 2; b.foo.z = 3; writefln(b.foo.toString()); c.foo = b.foo; writefln(c.foo.toString()); t.x = 4; t.y = 5; t.z = 6; writefln(t.toString()); b.foo = t; writefln(b.foo.toString()); } Note: The explicit "toString" calls are required because otherwise it prints the pointer address. Regan


Mar 18 2006
parent reply "Regan Heath" <regan netwin.co.nz> writes:
On Sat, 18 Mar 2006 23:14:46 +1300, Regan Heath <regan netwin.co.nz> wrote:
 3. you cannot call operators from pointers, without adding in *  
 everywhere.


I should mention that some operators, i.e. += don't work so well either. So, yeah, there are some problems with the pointer syntax. I think if you're running into these sorts of things you should consider using a class instead of a struct. Regan
 Partially true, i.e. when you're trying to add two Foo* together,  
 however you can add the following to the Foo struct to add a Foo* to a  
 Foo:

 	Foo opAdd(Foo rhs)
 	{
 		Foo n;
 		
 		n.x = x+rhs.x;
 		n.y = y+rhs.y;
 		n.z = z+rhs.z;
 	}
 	
 	Foo opAdd(Foo* rhs)
 	{
 		return opAdd(*rhs);
 	}	

 Regan


 In article <ops6i24bii23k2f5 nrage.netwin.co.nz>, Regan Heath says...
 On Thu, 16 Mar 2006 06:34:34 +0000 (UTC), Hong Wing
 <Hong_member pathlink.com> wrote:
 Thanks everyone here for your great work in D, especially Walter who
 makes it
 all happen.
 Here I am requesting a feature, which is "inout variable" and "inout
 return
 type"
 Rationale, for the example below,

 struct Foo
 {
 int x, int y, int z;
 }

 class Bar
 {
 private:
 Foo m_foo;
 public:
 Foo foo() { return m_foo; }
 Foo foo(Foo value) { return m_foo = value; }
 }

 now if i want to change the value of m_foo.x to 5, i have to do

 Foo foo = bar.foo;
 foo.x   = 5;
 bar.foo = foo;

 it would be nice if I can define a return type as "inout Foo"
 inout Foo foo() { return m_foo;}

 To extend it further, one might want to define inout variables as
 inout Foo foo = bar.foo;

 Advantages:

 1. efficiency, one can directly change the states of the struct  
 instead
 of
 making a copy first, change the copy, and put the result back to the
 struct

 2. less buggy, having to type 3 lines always increases chance of bugs,
 also,
 someone might inadvertantly do bar.foo.x = 5 and forgot that he is
 working on a
 copy

 3. reduce the use of pointers, the above can be achieved using  
 pointers,
 but it
 means defining two versions of the same method every time a struct
 property is
 written.

You're essentially asking for the ability to have variables which are references to structs (at least during assignment) and to return references to structs, all without using the pointer syntax. I think these things are un-necessary, and I think what you want can be achieved with pointers, without the penalty you mention in #3 above, see: import std.string; import std.stdio; struct Foo { int x; int y; int z; char[] toString() { return "("~std.string.toString(x)~","~std.string.toString(y)~","~std.string.toString(z)~")"; } } class Bar { private: Foo m_foo; public: Foo* foo() { return &m_foo; } Foo* foo(Foo* value) { m_foo = *value; return foo(); } Foo* foo(Foo value) { m_foo = value; return foo(); } } void main() { Bar b = new Bar(); Bar c = new Bar(); Foo t; b.foo.x = 1; b.foo.y = 2; b.foo.z = 3; writefln(b.foo.toString()); c.foo = b.foo; writefln(c.foo.toString()); t.x = 4; t.y = 5; t.z = 6; writefln(t.toString()); b.foo = t; writefln(b.foo.toString()); } Note: The explicit "toString" calls are required because otherwise it prints the pointer address. Regan



Mar 18 2006
parent reply Hong Wing <Hong_member pathlink.com> writes:
Hi, yes! exactly that's the problem for me, I am writing a protein modeling
software and use vector and matrices heavily, and I start to see things like:
*result = (*v1) + (*v2) * (*v3)

notice how all the * * * starts to make things very cluttered.

The problem also came about when I try to use DTL with structs, I cannot find a
way to change a matrix inside a vector efficiently without copying the whole
matrix out and copy it back. minTL supports lookup method to get a pointer to
the struct, on top of it's [] method.

Efficiency is essential for me,

Hong Wing

In article <ops6lufyj923k2f5 nrage.netwin.co.nz>, Regan Heath says...
On Sat, 18 Mar 2006 23:14:46 +1300, Regan Heath <regan netwin.co.nz> wrote:
 3. you cannot call operators from pointers, without adding in *  
 everywhere.


I should mention that some operators, i.e. += don't work so well either. So, yeah, there are some problems with the pointer syntax. I think if you're running into these sorts of things you should consider using a class instead of a struct. Regan
 Partially true, i.e. when you're trying to add two Foo* together,  
 however you can add the following to the Foo struct to add a Foo* to a  
 Foo:

 	Foo opAdd(Foo rhs)
 	{
 		Foo n;
 		
 		n.x = x+rhs.x;
 		n.y = y+rhs.y;
 		n.z = z+rhs.z;
 	}
 	
 	Foo opAdd(Foo* rhs)
 	{
 		return opAdd(*rhs);
 	}	

 Regan


 In article <ops6i24bii23k2f5 nrage.netwin.co.nz>, Regan Heath says...
 On Thu, 16 Mar 2006 06:34:34 +0000 (UTC), Hong Wing
 <Hong_member pathlink.com> wrote:
 Thanks everyone here for your great work in D, especially Walter who
 makes it
 all happen.
 Here I am requesting a feature, which is "inout variable" and "inout
 return
 type"
 Rationale, for the example below,

 struct Foo
 {
 int x, int y, int z;
 }

 class Bar
 {
 private:
 Foo m_foo;
 public:
 Foo foo() { return m_foo; }
 Foo foo(Foo value) { return m_foo = value; }
 }

 now if i want to change the value of m_foo.x to 5, i have to do

 Foo foo = bar.foo;
 foo.x   = 5;
 bar.foo = foo;

 it would be nice if I can define a return type as "inout Foo"
 inout Foo foo() { return m_foo;}

 To extend it further, one might want to define inout variables as
 inout Foo foo = bar.foo;

 Advantages:

 1. efficiency, one can directly change the states of the struct  
 instead
 of
 making a copy first, change the copy, and put the result back to the
 struct

 2. less buggy, having to type 3 lines always increases chance of bugs,
 also,
 someone might inadvertantly do bar.foo.x = 5 and forgot that he is
 working on a
 copy

 3. reduce the use of pointers, the above can be achieved using  
 pointers,
 but it
 means defining two versions of the same method every time a struct
 property is
 written.

You're essentially asking for the ability to have variables which are references to structs (at least during assignment) and to return references to structs, all without using the pointer syntax. I think these things are un-necessary, and I think what you want can be achieved with pointers, without the penalty you mention in #3 above, see: import std.string; import std.stdio; struct Foo { int x; int y; int z; char[] toString() { return "("~std.string.toString(x)~","~std.string.toString(y)~","~std.string.toString(z)~")"; } } class Bar { private: Foo m_foo; public: Foo* foo() { return &m_foo; } Foo* foo(Foo* value) { m_foo = *value; return foo(); } Foo* foo(Foo value) { m_foo = value; return foo(); } } void main() { Bar b = new Bar(); Bar c = new Bar(); Foo t; b.foo.x = 1; b.foo.y = 2; b.foo.z = 3; writefln(b.foo.toString()); c.foo = b.foo; writefln(c.foo.toString()); t.x = 4; t.y = 5; t.z = 6; writefln(t.toString()); b.foo = t; writefln(b.foo.toString()); } Note: The explicit "toString" calls are required because otherwise it prints the pointer address. Regan




Mar 18 2006
parent "Regan Heath" <regan netwin.co.nz> writes:
On Sat, 18 Mar 2006 11:02:53 +0000 (UTC), Hong Wing  
<Hong_member pathlink.com> wrote:
 Hi, yes! exactly that's the problem for me, I am writing a protein  
 modeling software and use vector and matrices heavily, and I start to  
 see things like:*result = (*v1) + (*v2) * (*v3)

 notice how all the * * * starts to make things very cluttered.

Yeah, I see the problem. You could call the operator overloads directly, eg. result = v1.opAdd(v2.opMul(v3)); but that's almost as bad. If you have opAdd(T *) and opMul(T *) then doesn't this work: *result = v1 + *v2 * v3; I just tried it, and it seems to (example below). However, isn't your bigger problem all the temporary structs being created and copied in the examples above? eg. (*v2) * (*v3) produces a temporary struct, which is then added to (*v1) producing another temporary struct, which is then copied to *result. Can the compiler ever optimise any of those temporaries away? Example: *result = (*v1) + (*v2) * (*v3) could be rewritten: *result = *v3; //copy struct *result *= v2; //opMulAssign(T *rhs) *result += v1; //opAddAssign(T *rhs) Here the opXAssign operators can access the rhs and add/mul them to/with result directly to avoid the temporary struct copy. Example: import std.string; import std.stdio; struct Foo { int x; int y; int z; char[] toString() { return "("~std.string.toString(x)~","~std.string.toString(y)~","~std.str ng.toString(z)~")"; } Foo opAdd(Foo rhs) { return opAdd(&rhs); } Foo opAdd(Foo* rhs) { Foo n; n.x = x+rhs.x; n.y = y+rhs.y; n.z = z+rhs.z; return n; } Foo opMul(Foo rhs) { return opAdd(&rhs); } Foo opMul(Foo* rhs) { Foo n; n.x = x*rhs.x; n.y = y*rhs.y; n.z = z*rhs.z; return n; } Foo* opAddAssign(Foo *rhs) { x += rhs.x; y += rhs.y; z += rhs.z; return this; } Foo* opMulAssign(Foo *rhs) { x *= rhs.x; y *= rhs.y; z *= rhs.z; return this; } } class Bar { private: Foo m_foo; public: Foo* foo() { return &m_foo; } Foo* foo(Foo* value) { m_foo = *value; return foo(); } Foo* foo(Foo value) { m_foo = value; return foo(); } } void main() { Foo* a = new Foo(); Bar b = new Bar(); Bar c = new Bar(); Bar d = new Bar(); b.foo.x = 1; b.foo.y = 1; b.foo.z = 1; c.foo.x = 2; c.foo.y = 2; c.foo.z = 2; d.foo.x = 3; d.foo.y = 3; d.foo.z = 3; *a = b.foo + *c.foo * d.foo; writefln("%s",(*a).toString()); *a = *d.foo; *a *= c.foo; *a += b.foo; writefln("%s",(*a).toString()); } Regan
Mar 18 2006