www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - still confused about call by reference

reply Hoenir <mrmocool gmx.de> writes:
I'm still a bit confused about call by reference. When do I have to 
explicitly use the & operator and when to use in, out and inout?
I want to convert my C++ vector struct to D:

struct vec3
{
	vec3 operator+(const vec3& v) const
	{return vec3(x+v.x, y+v.y, z+v.z);}
	vec3 operator-(const vec3& v) const
	{return vec3(x-v.x, y-v.y, z-v.z);}

I'm also wondering about how to handle the constness.
Thanks in advance for any help :)
Oct 28 2007
next sibling parent reply Derek Parnell <derek nomail.afraid.org> writes:
On Mon, 29 Oct 2007 04:32:44 +0100, Hoenir wrote:

 I'm still a bit confused about call by reference. When do I have to 
 explicitly use the & operator and when to use in, out and inout?
 I want to convert my C++ vector struct to D:
 
 struct vec3
 {
 	vec3 operator+(const vec3& v) const
 	{return vec3(x+v.x, y+v.y, z+v.z);}
 	vec3 operator-(const vec3& v) const
 	{return vec3(x-v.x, y-v.y, z-v.z);}
 
 I'm also wondering about how to handle the constness.
 Thanks in advance for any help :)

I'm no C++ user so I'm guessing a bit with the C++ syntax, but I'd code something like ... module vecs; struct vec3(VT) { private VT x,y,z; vec3 opAdd(const ref vec3 v) { vec3 t; t.x = x + v.x; t.y = y + v.y; t.z = z + v.z; return t; } vec3 opSub(const ref vec3 v) { vec3 t; t.x = x - v.x; t.y = y - v.y; t.z = z - v.z; return t; } void opCall(T,U,V)(const T a, const U b, const V c) { x = cast(VT)a; y = cast(VT)b; z = cast(VT)c; } private import std.string; string toString() { return std.string.format("[%s; %s; %s]", x,y,z); } } **** EXCEPT **** that 'const ref' crashes the compiler (see Bugzilla 1319) http://d.puremagic.com/issues/show_bug.cgi?id=1319 -- Derek (skype: derek.j.parnell) Melbourne, Australia 29/10/2007 2:58:45 PM
Oct 28 2007
parent reply Hoenir <mrmocool gmx.de> writes:
Derek Parnell schrieb:
     void opCall(T,U,V)(const T a, const U b, const V c)
     {
         x = cast(VT)a;
         y = cast(VT)b;
         z = cast(VT)c;
     }

vec3 opSub(vec3 v) {return vec3(x-v.x, y-v.y, z-v.z);} It doesn't work with vec3 opCall(U,V,W)(U a, V b, W c) { x = cast(T)a; y = cast(T)b; z = cast(T)c; return *this; } Tells me "type vec3!(real) is not an expression". (typedef double real) btw is it possible to return pointers?
Oct 30 2007
next sibling parent Hoenir <mrmocool gmx.de> writes:
Always wondered why it uses real. It's because I use the following typedef:

typedef vec3!(real) color;
Oct 30 2007
prev sibling parent reply Nathan Reed <nathaniel.reed gmail.com> writes:
Hoenir wrote:
 Derek Parnell schrieb:
     void opCall(T,U,V)(const T a, const U b, const V c)
     {
         x = cast(VT)a;
         y = cast(VT)b;
         z = cast(VT)c;
     }

vec3 opSub(vec3 v) {return vec3(x-v.x, y-v.y, z-v.z);} It doesn't work with vec3 opCall(U,V,W)(U a, V b, W c) { x = cast(T)a; y = cast(T)b; z = cast(T)c; return *this; } Tells me "type vec3!(real) is not an expression". (typedef double real) btw is it possible to return pointers?

I think the opCall needs to be declared static. Thanks, Nathan Reed
Oct 30 2007
parent reply Hoenir <mrmocool gmx.de> writes:
 I think the opCall needs to be declared static.
 

it static doesn't make any sense to me. Are you sure it must be static?
Oct 30 2007
next sibling parent Nathan Reed <nathaniel.reed gmail.com> writes:
Hoenir wrote:
 I think the opCall needs to be declared static.

it static doesn't make any sense to me. Are you sure it must be static?

Right, it's used as a constructor. That's the point: it has to be callable without having an instance of the struct laying around - hence static. With a non-static opCall, you can use an instance of the struct as a callable entity. Static opCall lets you use the name of the struct itself as a callable entity. Thanks, Nathan Reed
Oct 30 2007
prev sibling parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Hoenir" <mrmocool gmx.de> wrote in message 
news:fg7o1e$2lhs$1 digitalmars.com...
 I think the opCall needs to be declared static.

static doesn't make any sense to me. Are you sure it must be static?

This is the stupid compromise that we got instead of struct ctors in D1. Use a static opCall, Walter says, it'll get optimized out. That's great, but it makes it impossible to initialize an instance of a struct on the heap without factoring out the initialization to _another_ function, and it's just another stupid inconsistency which we'll have to live with even though D2 will have struct ctors. *sigh* You cacn read all about it in "Dynamic Initializaion of Structs" here: http://www.digitalmars.com/d/1.0/struct.html
Oct 30 2007
next sibling parent reply "Saaa" <empty needmail.com> writes:
What does this mean exactly ?

initialize an instance of a struct on the heap
without factoring out the initialization to _another_ function
Oct 30 2007
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Saaa wrote:
 What does this mean exactly ?
 
 initialize an instance of a struct on the heap
 without factoring out the initialization to _another_ function

You can say MyStruct *x = new MyStruct; But even with a static opCall defined, this doesn't work MyStruct *x = new MyStruct(a,b,c); You have to do something like: MyStruct *x = new MyStruct; *x = MyStruct(a,b,c); Or that's what I guess he means, at least. I haven't actually tried the code above to see what it will do. --bb
Oct 30 2007
parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Bill Baxter" <dnewsgroup billbaxter.com> wrote in message 
news:fg8mpg$15gl$1 digitalmars.com...
 Saaa wrote:
 What does this mean exactly ?

 initialize an instance of a struct on the heap
 without factoring out the initialization to _another_ function

You can say MyStruct *x = new MyStruct; But even with a static opCall defined, this doesn't work MyStruct *x = new MyStruct(a,b,c); You have to do something like: MyStruct *x = new MyStruct; *x = MyStruct(a,b,c); Or that's what I guess he means, at least. I haven't actually tried the code above to see what it will do. --bb

I was thinking more along the lines of struct S { int x, y; void init(int x, int y) { this.x = x; this.y = y; } static S opCall(int x, int y) { S s; s.init(x, y); return s; } } .. // Stack S s = S(3, 4); // Heap S* t = new S; t.init(5, 6);
Oct 30 2007
parent "Saaa" <empty needmail.com> writes:
 Saaa wrote:
 What does this mean exactly ?

 initialize an instance of a struct on the heap
 without factoring out the initialization to _another_ function

You can say MyStruct *x = new MyStruct; But even with a static opCall defined, this doesn't work MyStruct *x = new MyStruct(a,b,c); You have to do something like: MyStruct *x = new MyStruct; *x = MyStruct(a,b,c); Or that's what I guess he means, at least. I haven't actually tried the code above to see what it will do. --bb

I was thinking more along the lines of struct S { int x, y; void init(int x, int y) { this.x = x; this.y = y; } static S opCall(int x, int y) { S s; s.init(x, y); return s; } } .. // Stack S s = S(3, 4); // Heap S* t = new S; t.init(5, 6);

Ah, I see what you mean. making your own _new would fix this, right? But yeah I think a constructor would be much better ;) I always put everything on the stack for speed purposes. I now that I think about it, I also never throw any struct array away. I try to allocate all necessary memory at the beginning of my programs. Ticks are allot more scarce than memory in my programs :D Anyway thanks.
Oct 30 2007
prev sibling parent reply Hoenir <mrmocool gmx.de> writes:
 You can read all about it in "Dynamic Initializaion of Structs" here: 
 http://www.digitalmars.com/d/1.0/struct.html 
 

Though I don't really get the purpose of opCall. For normal member initialization struct literals are completely sufficient. opCall would just make sense as a copy constructor, but this does not work. " static S opCall(S v) { S s; s.a = v.a + 1; return s; } } S s = 3; // sets s.a to 3 S t = s; // sets t.a to 3, S.opCall(s) is not called"
Oct 31 2007
parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Hoenir" <mrmocool gmx.de> wrote in message 
news:fgb3h9$2q19$1 digitalmars.com...
 You can read all about it in "Dynamic Initializaion of Structs" here: 
 http://www.digitalmars.com/d/1.0/struct.html

Though I don't really get the purpose of opCall. For normal member initialization struct literals are completely sufficient. opCall would just make sense as a copy constructor, but this does not work.

Struct literals were added after static opCall was 'blessed', so static opCall was the only way to fly for a while. Even then, the dynamic struct literals use a completely different syntax from the static struct initializers (another big *sigh*). But it's still useful to have a constructor function to i.e. check valid values, perform preprocessing on the values, fill in other members based on values that you give, etc.
Oct 31 2007
parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Jarrett Billingsley" <kb3ctd2 yahoo.com> wrote in message 
news:fgb48g$2rqq$1 digitalmars.com...
 "Hoenir" <mrmocool gmx.de> wrote in message 
 news:fgb3h9$2q19$1 digitalmars.com...
 You can read all about it in "Dynamic Initializaion of Structs" here: 
 http://www.digitalmars.com/d/1.0/struct.html

Though I don't really get the purpose of opCall. For normal member initialization struct literals are completely sufficient. opCall would just make sense as a copy constructor, but this does not work.

Struct literals were added after static opCall was 'blessed', so static opCall was the only way to fly for a while. Even then, the dynamic struct literals use a completely different syntax from the static struct initializers (another big *sigh*). But it's still useful to have a constructor function to i.e. check valid values, perform preprocessing on the values, fill in other members based on values that you give, etc.

Oh and I guess I should mention, struct to struct assignment is always defined as a bit copy. You can't intercept it in any way.
Oct 31 2007
parent Hoenir <mrmocool gmx.de> writes:
 But it's still useful to have a 
 constructor function to i.e. check valid values, perform preprocessing on 
 the values, fill in other members based on values that you give, etc.


afford to check values etc. This vector class is supposed to be used in a ray tracing application.
 Oh and I guess I should mention, struct to struct assignment is always 
 defined as a bit copy.  You can't intercept it in any way. 
 

Oct 31 2007
prev sibling parent reply Nathan Reed <nathaniel.reed gmail.com> writes:
Hoenir wrote:
 I'm still a bit confused about call by reference. When do I have to 
 explicitly use the & operator and when to use in, out and inout?
 I want to convert my C++ vector struct to D:
 
 struct vec3
 {
     vec3 operator+(const vec3& v) const
     {return vec3(x+v.x, y+v.y, z+v.z);}
     vec3 operator-(const vec3& v) const
     {return vec3(x-v.x, y-v.y, z-v.z);}
 
 I'm also wondering about how to handle the constness.
 Thanks in advance for any help :)

In C++, const references are used to signal the data is not changed by the function, and is passed by reference purely for efficiency's sake. I believe the correct D equivalent is 'in'. The D spec states that 'in' is equivalent to 'final const scope', which means writing to the parameter is prevented, as in C++. The spec does not say whether 'in' results in pass-by-reference or not, but in this case, I believe that is a decision that should be made by the compiler, not the programmer. C++ non-const references are used to signal out-parameters, so the D equivalent would be out or inout, depending on whether your function wants to use the value that is passed in or not - 'out' parameters are initialized to their type's default value when the function begins, so any value that was there when the function was called gets clobbered, while 'inout' parameters let you read the original value. Honestly, I'm not entirely sure what D's 'ref' parameter-storage class is for, since AFAICT all the uses of pass-by-reference are covered by in, out, and inout. Thanks, Nathan Reed
Oct 28 2007
next sibling parent reply Hoenir <mrmocool gmx.de> writes:
Nathan Reed schrieb:
 Hoenir wrote:
 I'm still a bit confused about call by reference. When do I have to 
 explicitly use the & operator and when to use in, out and inout?
 I want to convert my C++ vector struct to D:

 struct vec3
 {
     vec3 operator+(const vec3& v) const
     {return vec3(x+v.x, y+v.y, z+v.z);}
     vec3 operator-(const vec3& v) const
     {return vec3(x-v.x, y-v.y, z-v.z);}

 I'm also wondering about how to handle the constness.
 Thanks in advance for any help :)

In C++, const references are used to signal the data is not changed by the function, and is passed by reference purely for efficiency's sake. I believe the correct D equivalent is 'in'. The D spec states that 'in' is equivalent to 'final const scope', which means writing to the parameter is prevented, as in C++. The spec does not say whether 'in' results in pass-by-reference or not, but in this case, I believe that is a decision that should be made by the compiler, not the programmer. C++ non-const references are used to signal out-parameters, so the D equivalent would be out or inout, depending on whether your function wants to use the value that is passed in or not - 'out' parameters are initialized to their type's default value when the function begins, so any value that was there when the function was called gets clobbered, while 'inout' parameters let you read the original value. Honestly, I'm not entirely sure what D's 'ref' parameter-storage class is for, since AFAICT all the uses of pass-by-reference are covered by in, out, and inout. Thanks, Nathan Reed

and if you add in or out it means the pointer rather than the data.
Oct 29 2007
parent reply Nathan Reed <nathaniel.reed gmail.com> writes:
Hoenir wrote:
 I think I read somewhere, objects are automatically passed by reference 
 and if you add in or out it means the pointer rather than the data.

Object types in D are indeed reference types, so passing these by reference would mean you'd pass a reference to a reference. (This is useless for 'in' parameters, but allows the expected behavior for 'out' parameters.) Of course, references are implemented as pointers, but they're semantically distinct from pointers. Thanks, Nathan Reed
Oct 29 2007
parent reply Hoenir <mrmocool gmx.de> writes:
 Object types in D are indeed reference types
 

"Whereas classes are reference types, structs are value types." so would I have to use the & operator?
Oct 31 2007
parent reply Nathan Reed <nathaniel.reed gmail.com> writes:
Hoenir wrote:
 Object types in D are indeed reference types

"Whereas classes are reference types, structs are value types." so would I have to use the & operator?

That gets you a pointer. You don't need & to pass a value type to a 'ref' parameter.
Oct 31 2007
parent reply Hoenir <mrmocool gmx.de> writes:
 That gets you a pointer.  You don't need & to pass a value type to a 
 'ref' parameter.

No I meant the argument passing: void foo(Struct& S) {...}
Oct 31 2007
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Hoenir wrote:
 That gets you a pointer.  You don't need & to pass a value type to a 
 'ref' parameter.

No I meant the argument passing: void foo(Struct& S) {...}

In D that's written: void foo(ref Struct S) {...} And called like: Struct s; foo(s); --bb
Oct 31 2007
parent reply Hoenir <mrmocool gmx.de> writes:
 In D that's written:
 
 void foo(ref Struct S)
 {...}
 
 And called like:
 
 Struct s;
 foo(s);
 
 --bb

Ok, but ref is an alias for inout atm. And I think "in" is implicitly used if nothing other is specified, isn't it? So how would I pass a struct by reference but without being able to write (const & in C++)? and does this make sense(why is struct a value type)? Please excuse my noob questions. :-)
Nov 01 2007
next sibling parent "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Hoenir" <mrmocool gmx.de> wrote in message 
news:fgcaem$2rju$1 digitalmars.com...
 In D that's written:

 void foo(ref Struct S)
 {...}

 And called like:

 Struct s;
 foo(s);

 --bb

Ok, but ref is an alias for inout atm. And I think "in" is implicitly used if nothing other is specified, isn't it? So how would I pass a struct by reference but without being able to write (const & in C++)? and does this make sense(why is struct a value type)? Please excuse my noob questions. :-)

Your question was answered in the very first reply to your OP, by Derek: void foo(const ref S s) { } And it was also mentioned that 'const ref' crashes the compiler.
Nov 01 2007
prev sibling parent Bill Baxter <dnewsgroup billbaxter.com> writes:
Hoenir wrote:
 In D that's written:

 void foo(ref Struct S)
 {...}

 And called like:

 Struct s;
 foo(s);

 --bb

Ok, but ref is an alias for inout atm. And I think "in" is implicitly used if nothing other is specified, isn't it?

Yes.
 So how would I pass a struct by reference but without being able to 
 write (const & in C++)? and does this make sense(why is struct a value 
 type)?
 
 Please excuse my noob questions. :-)

Sorry, I'm using D1.x. What you ask is not possible with D1.x. I expect as others have said that "const ref" will do it in D2.x as soon as that stops crashing the compiler. --bb
Nov 02 2007
prev sibling parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Nathan Reed" <nathaniel.reed gmail.com> wrote in message 
news:fg40ac$2fuo$1 digitalmars.com...
 Honestly, I'm not entirely sure what D's 'ref' parameter-storage class is 
 for, since AFAICT all the uses of pass-by-reference are covered by in, 
 out, and inout.

'ref' is an alias of 'inout' at least for the time being. It was introduced for forwards compatibility, as it seems likely that we will be getting reference returns, and returning an 'inout' doesn't really make much sense.
Oct 29 2007
parent Bill Baxter <dnewsgroup billbaxter.com> writes:
Jarrett Billingsley wrote:
 "Nathan Reed" <nathaniel.reed gmail.com> wrote in message 
 news:fg40ac$2fuo$1 digitalmars.com...
 Honestly, I'm not entirely sure what D's 'ref' parameter-storage class is 
 for, since AFAICT all the uses of pass-by-reference are covered by in, 
 out, and inout.

'ref' is an alias of 'inout' at least for the time being. It was introduced for forwards compatibility, as it seems likely that we will be getting reference returns, and returning an 'inout' doesn't really make much sense.

Also 'const inout' as a parameter doesn't make much sense either. --bb
Oct 29 2007