www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - "not an lvalue"

reply CrypticMetaphor <CrypticMetaphor88 gmail.com> writes:
Hi, I've been away from D for a while, but now I'm back and I'm stuck 
with an compile time error.

I've got a Matrix33 class and a Vector3 class, but something is wrong 
with the way I return my Vector3 in my matrix class:

If I do this I get an error:

Matrix33 mtest = new Matrix33();
mtest.SetIdentity();
Vector3 test1 = new Vector3(0, 0, 0);
Vector3 test2 = test + mtest.GetColumn(2);

I get the error "Error: mtest.GetColumn(x) is not an lvalue"

But the following works:

Matrix33 mtest = new Matrix33();
mtest.SetIdentity();
Vector3 test1 = new Vector3(0, 0, 0);
Vector3 temp = mtest.GetColumn(2);
Vector3 test2 = test + temp;

// GetColumn method
Matrix33
{
// ...

   /// Get a matrix column, horizontal line
   Vector3 GetColumn(uint index)
   {
     assert(!(index > 2), "index is too high");
     return new Vector3(cell[index * 3], cell[index * 3 + 1], cell[index 
* 3 + 2]);
   }
// ...
}

My questions:
What changes do I have to make to make the first example compile?
May 01 2011
next sibling parent reply Peter Alexander <peter.alexander.au gmail.com> writes:
On 1/05/11 2:30 PM, CrypticMetaphor wrote:
 Hi, I've been away from D for a while, but now I'm back and I'm stuck
 with an compile time error.

 I've got a Matrix33 class and a Vector3 class, but something is wrong
 with the way I return my Vector3 in my matrix class:

 If I do this I get an error:

 Matrix33 mtest = new Matrix33();
 mtest.SetIdentity();
 Vector3 test1 = new Vector3(0, 0, 0);
 Vector3 test2 = test + mtest.GetColumn(2);

 I get the error "Error: mtest.GetColumn(x) is not an lvalue"

 But the following works:

 Matrix33 mtest = new Matrix33();
 mtest.SetIdentity();
 Vector3 test1 = new Vector3(0, 0, 0);
 Vector3 temp = mtest.GetColumn(2);
 Vector3 test2 = test + temp;

 // GetColumn method
 Matrix33
 {
 // ...

 /// Get a matrix column, horizontal line
 Vector3 GetColumn(uint index)
 {
 assert(!(index > 2), "index is too high");
 return new Vector3(cell[index * 3], cell[index * 3 + 1], cell[index * 3
 + 2]);
 }
 // ...
 }

 My questions:
 What changes do I have to make to make the first example compile?

My guess is that your Vector3.opBinary!"+" is taking "ref" arguments instead of just taking them by value. Can you show us that function?
May 01 2011
parent CrypticMetaphor <CrypticMetaphor88 gmail.com> writes:
Yes, you were right:

Vector3
{
// ...
/// Binary operator for operator + and -
public Vector3 opBinary(string op) (const ref Vector3 rhs) const if (op 
== "+" || op == "-")
   {
     mixin("return new Vector3(x" ~ op ~ "rhs.x, y" ~ op ~ "rhs.y, z" ~ 
op ~ "rhs.z);");
   }
// ...
}

I removed ref and it worked. I'm a bit confused by this, I'll go read up 
on the ref keyword.
May 01 2011
prev sibling next sibling parent reply Dmitry Olshansky <dmitry.olsh gmail.com> writes:
On 01.05.2011 17:30, CrypticMetaphor wrote:
 Hi, I've been away from D for a while, but now I'm back and I'm stuck 
 with an compile time error.

 I've got a Matrix33 class and a Vector3 class, but something is wrong 
 with the way I return my Vector3 in my matrix class:

 If I do this I get an error:

 Matrix33 mtest = new Matrix33();
 mtest.SetIdentity();
 Vector3 test1 = new Vector3(0, 0, 0);
 Vector3 test2 = test + mtest.GetColumn(2);

 I get the error "Error: mtest.GetColumn(x) is not an lvalue"

 But the following works:

 Matrix33 mtest = new Matrix33();
 mtest.SetIdentity();
 Vector3 test1 = new Vector3(0, 0, 0);
 Vector3 temp = mtest.GetColumn(2);
 Vector3 test2 = test + temp;

 // GetColumn method
 Matrix33
 {
 // ...

   /// Get a matrix column, horizontal line
   Vector3 GetColumn(uint index)
   {
     assert(!(index > 2), "index is too high");
     return new Vector3(cell[index * 3], cell[index * 3 + 1], 
 cell[index * 3 + 2]);
   }
 // ...
 }

 My questions:
 What changes do I have to make to make the first example compile?

lightweight & plain data things like vectors. There are structs for that. In general, structs are value-like objects living on the stack while classes are reference-like objects living on the heap. Your current code is going to allocate on GC heap new vector in every GetColumn and I suspect also when adding two vectors. As for the error I think the reason is in your's Vector's operator + overloading which expects ref Vector parameter it seems, though you'd better provide this function code as well. -- Dmitry Olshansky
May 01 2011
next sibling parent CrypticMetaphor <CrypticMetaphor88 gmail.com> writes:
On 5/1/2011 3:53 PM, Dmitry Olshansky wrote:
 Ehm.. Well, first things first: you shouldn't use classes for
 lightweight & plain data things like vectors. There are structs for
 that. In general, structs are value-like objects living on the stack
 while classes are reference-like objects living on the heap.  Your
 current code is going to allocate on GC heap new vector in every
 GetColumn and I suspect also when adding two vectors.

Yes, I will change that, thank you. Btw, I'd like to mention that in addition to being allocated on the heap, classes also be 2 pointers larger because of the hidden __vptr and __monitor.
May 01 2011
prev sibling parent reply Peter Alexander <peter.alexander.au gmail.com> writes:
On 1/05/11 2:53 PM, Dmitry Olshansky wrote:
 Ehm.. Well, first things first: you shouldn't use classes for
 lightweight & plain data things like vectors. There are structs for
 that. In general, structs are value-like objects living on the stack
 while classes are reference-like objects living on the heap. Your
 current code is going to allocate on GC heap new vector in every
 GetColumn and I suspect also when adding two vectors.

structs don't live on the stack. They live wherever they are told to. In fact most structs will live in the heap as parts of class objects, or other heap-allocated objects such as dynamic arrays or AAs. The stack is for local variables and other things to do with the local scope. While struct objects *can* go on the stack, it is false to say that structs live on the stack and classes live on the heap. The type of an object (value type or reference type) is orthogonal to its storage location and the two should not be conflated.
May 01 2011
parent Dmitry Olshansky <dmitry.olsh gmail.com> writes:
On 01.05.2011 19:31, Peter Alexander wrote:
 On 1/05/11 2:53 PM, Dmitry Olshansky wrote:
 Ehm.. Well, first things first: you shouldn't use classes for
 lightweight & plain data things like vectors. There are structs for
 that. In general, structs are value-like objects living on the stack
 while classes are reference-like objects living on the heap. Your
 current code is going to allocate on GC heap new vector in every
 GetColumn and I suspect also when adding two vectors.

structs don't live on the stack. They live wherever they are told to. In fact most structs will live in the heap as parts of class objects, or other heap-allocated objects such as dynamic arrays or AAs. The stack is for local variables and other things to do with the local scope.

pour all this rather involved details on the OP. That's what this 'In general' implied ;) Now I think I better have left the 'usually lives' part where it was before posting. Yet you're right, I see the misleading core of such statements.
 While struct objects *can* go on the stack, it is false to say that 
 structs live on the stack and classes live on the heap. The type of an 
 object (value type or reference type) is orthogonal to its storage 
 location and the two should not be conflated.

allocating class objects. Damn, it looked like I was the only one considering class storage anywhere not on the GC heap as usual thing. Everyone else observed that 90% of use cases are going to be heap allocations with new. But what I actually had in mind was that when you see: ... A a; ... For struct means that it's stored directly at this 'memory scope' be it static, class member or anything (denote 'here'). For classes that merely means it's reference stored 'here', the object itself resides elsewhere, the heap being that most likely possibility. Again, sorry for any confusion I might have caused. -- Dmitry Olshansky
May 01 2011
prev sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Sun, 01 May 2011 09:30:34 -0400, CrypticMetaphor  
<CrypticMetaphor88 gmail.com> wrote:

 Hi, I've been away from D for a while, but now I'm back and I'm stuck  
 with an compile time error.

 I've got a Matrix33 class and a Vector3 class, but something is wrong  
 with the way I return my Vector3 in my matrix class:

 If I do this I get an error:

 Matrix33 mtest = new Matrix33();
 mtest.SetIdentity();
 Vector3 test1 = new Vector3(0, 0, 0);
 Vector3 test2 = test + mtest.GetColumn(2);

 I get the error "Error: mtest.GetColumn(x) is not an lvalue"

 But the following works:

 Matrix33 mtest = new Matrix33();
 mtest.SetIdentity();
 Vector3 test1 = new Vector3(0, 0, 0);
 Vector3 temp = mtest.GetColumn(2);
 Vector3 test2 = test + temp;

 // GetColumn method
 Matrix33
 {
 // ...

    /// Get a matrix column, horizontal line
    Vector3 GetColumn(uint index)
    {
      assert(!(index > 2), "index is too high");
      return new Vector3(cell[index * 3], cell[index * 3 + 1], cell[index  
 * 3 + 2]);
    }
 // ...
 }

 My questions:
 What changes do I have to make to make the first example compile?

I think others have said you need to not use ref. The solution (which is not yet properly implemented) is to use auto ref. The way it is supposed to work is like this: void foo(T t); // t is passed by value (copied). t can be an lvalue or an rvalue void foo(ref T t); // t is passed by reference. t can only be an lvalue void foo(auto ref T t); // t is passed by reference. t can be an lvalue or an rvalue. Essentially, with auto-ref you are saying, I know there are dangers in passing rvalues here, but I still want to accept an rvalue by reference. There was confusion on Walter's part as to how this was supposed to work, so auto ref currently doesn't act this way. There are two workarounds I know of: 1. as you have done, convert the rvalue to an lvalue by assigning to a temporary 2. call the method on the rvalue. For some reason (probably to make people not go insane) you are allowed to pass an rvalue by ref if you are calling a method on it. i.e. this should work (don't remember vectors much, but I believe addition is commutative?): Vector3 test2 = mtest.GetColumn(2) + test; If you can't do the second method, I think method 1 is your only option. -Steve
May 02 2011