www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Array operations with array of structs

reply "Peter" <default email.com> writes:
Hi,
I have a struct with arithmetic operations defined using opBinary 
but array operations with arrays of it don't work.

struct Vector3 {
     public double[3] _p;
     ...
     Vector3 opBinary(string op)(in Vector3 rhs) const
     if (op == "+"){
         Vector3 result;
         result._p[] = this._p[] + rhs._p[];
         return result;
     }
     ...
}

unittest{
     auto a = Vector3([2.0, 2.0, 0.0]);
     auto b = Vector3([1.0, 2.0, 1.0]);
     Vector3[] c = [a];
     Vector3[] d = [b];
     Vector3 e = a + b; // works
     Vector3[] f;
     f[] = c[] + d[]; // Error: invalid array operation 'f[] = c[] 
+ d[]' because Vector3 doesn't support necessary arithmetic 
operations
}

how can I get this to work?

Thanks
Jul 05 2015
next sibling parent reply "Nicholas Wilson" <iamthewilsonator hotmail.com> writes:
On Monday, 6 July 2015 at 01:16:54 UTC, Peter wrote:
 Hi,
 I have a struct with arithmetic operations defined using 
 opBinary but array operations with arrays of it don't work.

 struct Vector3 {
     public double[3] _p;
     ...
     Vector3 opBinary(string op)(in Vector3 rhs) const
     if (op == "+"){
         Vector3 result;
         result._p[] = this._p[] + rhs._p[];
         return result;
     }
     ...
 }

 unittest{
     auto a = Vector3([2.0, 2.0, 0.0]);
     auto b = Vector3([1.0, 2.0, 1.0]);
     Vector3[] c = [a];
     Vector3[] d = [b];
     Vector3 e = a + b; // works
     Vector3[] f;
     f[] = c[] + d[]; // Error: invalid array operation 'f[] = 
 c[] + d[]' because Vector3 doesn't support necessary arithmetic 
 operations
 }

 how can I get this to work?

 Thanks
you need to define the slice operators. e.g. auto opSlice() // no parameters to get whole slice eg. a[] // can also define with opSlice(size_t i, size_t j) // for access like a[ i .. j] { return _p; }
Jul 05 2015
next sibling parent "Nicholas Wilson" <iamthewilsonator hotmail.com> writes:
On Monday, 6 July 2015 at 03:02:59 UTC, Nicholas Wilson wrote:
 On Monday, 6 July 2015 at 01:16:54 UTC, Peter wrote:
 Hi,
 I have a struct with arithmetic operations defined using 
 opBinary but array operations with arrays of it don't work.

 struct Vector3 {
     public double[3] _p;
     ...
     Vector3 opBinary(string op)(in Vector3 rhs) const
     if (op == "+"){
         Vector3 result;
         result._p[] = this._p[] + rhs._p[];
         return result;
     }
     ...
 }

 unittest{
     auto a = Vector3([2.0, 2.0, 0.0]);
     auto b = Vector3([1.0, 2.0, 1.0]);
     Vector3[] c = [a];
     Vector3[] d = [b];
     Vector3 e = a + b; // works
     Vector3[] f;
     f[] = c[] + d[]; // Error: invalid array operation 'f[] = 
 c[] + d[]' because Vector3 doesn't support necessary 
 arithmetic operations
 }

 how can I get this to work?

 Thanks
you need to define the slice operators. e.g. auto opSlice() // no parameters to get whole slice eg. a[] // can also define with opSlice(size_t i, size_t j) // for access like a[ i .. j] { return _p; }
whoops. it may be complaining about lack of opSliceAssign i.e. `f[] =` again define this with no parameters for the whole slice or two for a[i .. j] = also note that you need to give memory for this array assignment to go. eg.
     auto a = Vector3([2.0, 2.0, 0.0]);
     auto b = Vector3([1.0, 2.0, 1.0]);
     Vector3[] c = [a];
     Vector3[] d = [b];
     Vector3 e = a + b; // works
     Vector3[] f;
     f[] = c[] + d[];
will likely crash because `f` doesn't point to any allocated memory. initialising like Vector3[] f = [Vector3()]; Enhancement Request to tell you the names of the missing methods required filed at https://issues.dlang.org/show_bug.cgi?id=14772
Jul 05 2015
prev sibling parent "anonymous" <anonymous example.com> writes:
On Monday, 6 July 2015 at 03:02:59 UTC, Nicholas Wilson wrote:
 On Monday, 6 July 2015 at 01:16:54 UTC, Peter wrote:
[...]
 unittest{
     auto a = Vector3([2.0, 2.0, 0.0]);
     auto b = Vector3([1.0, 2.0, 1.0]);
     Vector3[] c = [a];
     Vector3[] d = [b];
     Vector3 e = a + b; // works
     Vector3[] f;
     f[] = c[] + d[]; // Error: invalid array operation 'f[] = 
 c[] + d[]' because Vector3 doesn't support necessary 
 arithmetic operations
 }

 how can I get this to work?

 Thanks
you need to define the slice operators. e.g. auto opSlice() // no parameters to get whole slice eg. a[] // can also define with opSlice(size_t i, size_t j) // for access like a[ i .. j] { return _p; }
`f`, `c`, and `d` are arrays though, not `Vector3`s. The code doesn't try to slice a Vector3. So as far as I can see, there's no point in adding opSlice to it.
Jul 06 2015
prev sibling parent reply "anonymous" <anonymous example.com> writes:
On Monday, 6 July 2015 at 01:16:54 UTC, Peter wrote:
 Hi,
 I have a struct with arithmetic operations defined using 
 opBinary but array operations with arrays of it don't work.

 struct Vector3 {
     public double[3] _p;
     ...
     Vector3 opBinary(string op)(in Vector3 rhs) const
     if (op == "+"){
         Vector3 result;
         result._p[] = this._p[] + rhs._p[];
         return result;
     }
     ...
 }

 unittest{
     auto a = Vector3([2.0, 2.0, 0.0]);
     auto b = Vector3([1.0, 2.0, 1.0]);
     Vector3[] c = [a];
     Vector3[] d = [b];
     Vector3 e = a + b; // works
     Vector3[] f;
     f[] = c[] + d[]; // Error: invalid array operation 'f[] = 
 c[] + d[]' because Vector3 doesn't support necessary arithmetic 
 operations
 }

 how can I get this to work?

 Thanks
Works for me with various versions of dmd on linux. What compiler are you using, what version of it, what operating system, etc?
Jul 06 2015
parent reply "Peter" <default email.com> writes:
On Monday, 6 July 2015 at 10:29:35 UTC, anonymous wrote:
 Works for me with various versions of dmd on linux. What 
 compiler are you using, what version of it, what operating 
 system, etc?
dmd 2.066.1, windows 7 64bit
Jul 06 2015
parent reply "anonymous" <anonymous example.com> writes:
On Monday, 6 July 2015 at 12:15:22 UTC, Peter wrote:
 dmd 2.066.1, windows 7 64bit
Tested it on Windows 7, using dmd 2.066.1: works for me. The exact code I tested (just commented those "..." out): ---- struct Vector3 { public double[3] _p; //... Vector3 opBinary(string op)(in Vector3 rhs) const if (op == "+"){ Vector3 result; result._p[] = this._p[] + rhs._p[]; return result; } //... } unittest{ auto a = Vector3([2.0, 2.0, 0.0]); auto b = Vector3([1.0, 2.0, 1.0]); Vector3[] c = [a]; Vector3[] d = [b]; Vector3 e = a + b; Vector3[] f; f[] = c[] + d[]; } ---- And the command line: dmd -main -unittest test.d
Jul 06 2015
parent reply "Peter" <default email.com> writes:
On Monday, 6 July 2015 at 15:48:28 UTC, anonymous wrote:

Ok, I disabled everything in the struct except what I posted and 
it ran.
I then uncommented stuff to isolate the cause. I've added in the 
bits that cause the error below (plus some constructors just for 
reference).

struct Vector3 {
     public double[3] _p;
      nogc this(in double[] p)
     {
	switch ( p.length ) {
	case 0: _p[0] = _p[1] = _p[2] = 0.0; break;
	case 1: _p[0] = p[0]; _p[1] = _p[2] = 0.0; break;
	case 2: _p[0] = p[0]; _p[1] = p[1]; _p[2] = 0.0; break;
	default: _p[0] = p[0]; _p[1] = p[1]; _p[2] = p[2]; break;
	}
     }
      nogc this(in double p0, in double p1=0.0, in double p2=0.0)
     {
  	_p[0] = p0;
	_p[1] = p1;
	_p[2] = p2;
     }
      nogc this(in Vector3 other)
     {
	_p[] = other._p[];
     }
     //...
     Vector3 opBinary(string op)(in Vector3 rhs) const
     if (op == "+"){
         Vector3 result;
         result._p[] = this._p[] + rhs._p[];
         return result;
     }
     // The bits that cause the error:
     //... Enabling any one (or more) of the following causes the 
error
     this(this)
     {
	_p = _p.dup;
     }

      nogc ref Vector3 opAssign(ref Vector3 rhs)
     {
	_p[] = rhs._p[];
	 return this;
     }

      nogc ref Vector3 opAssign(Vector3 rhs)
     {
	_p[] = rhs._p[];
	return this;
     }
     //...
}

Any ideas about what's happening?
Jul 07 2015
parent reply "ketmar" <ketmar ketmar.no-ip.org> writes:
On Tue, 07 Jul 2015 11:09:52 +0000, Peter wrote:

 Any ideas about what's happening?
yes. there is code in "arrayop.c" that tells: // Built-in array ops should be trusted, pure, nothrow and nogc StorageClass stc = STCtrusted | STCpure | STCnothrow | STCnogc; under the hoods compiler rewrites `f[] = c[]+d[];` to this: _arraySliceSliceAddSliceAssign_S3z007Vector3 extern (C) Vector3[] (Vector3[] p2, const Vector3[] p1, const Vector3[] p0) pure nothrow nogc trusted { foreach (p; 0u .. p2.length) { p2[p] = cast(Vector3)(p0[p] + p1[p]); } return p2; } _arraySliceSliceAddSliceAssign_S3z007Vector3(f, c, d); do you see the gotcha? if you uncomment postblit or assigns, this build function fails to compile, as that operations aren't "pure nothrow nogc trusted", and they will be used for either assign or postblitting. the same will happen if you add anything to your `opBinary` which make it unpure/ system (like `writeln`, for example). i.e. Vector3 opBinary(string op : "+") (in Vector3 rhs) const { import std.stdio; writeln("opBinary"); // (1) Vector3 result; result.vdata[] = this.vdata[] + rhs.vdata[]; return result; } BOOM! adding (1) is enough to get the same error. this is what is going on. i don't know why DMD insists on such restrictions for array operations, though.
Jul 07 2015
parent reply "Peter" <default email.com> writes:
On Wednesday, 8 July 2015 at 06:05:54 UTC, ketmar wrote:
 do you see the gotcha? if you uncomment postblit or assigns, 
 this build function fails to compile, as that operations aren't 
 "pure nothrow  nogc  trusted", and they will be used for either 
 assign or postblitting.
So after looking into it a little bit... It looks like the opAssigns can take all the attributes without throwing errors so that's good. The postblit can only not take nogc due to the array duplication which is understandable. I think the postblit might be redundant anyway since the struct is built on a static array so there is no possibility of two different Vect3s "pointing" to the same data. Can someone confirm or does the postblit do anything else?
Jul 11 2015
next sibling parent "anonymous" <anonymous example.com> writes:
On Saturday, 11 July 2015 at 13:31:12 UTC, Peter wrote:
 The postblit can only not take  nogc due to the array 
 duplication which is understandable.
 I think the postblit might be redundant anyway since the struct 
 is built on a static array so there is no possibility of two 
 different Vect3s "pointing" to the same data.
 Can someone confirm or does the postblit do anything else?
The postblit is pointless, yes. The `.dup` copies from one location to another, only for the assignment to copy everything back to the original location; and then the new array is discarded.
Jul 11 2015
prev sibling parent "Peter" <default email.com> writes:
On Saturday, 11 July 2015 at 13:31:12 UTC, Peter wrote:
 So after looking into it a little bit...
So now I'm trying to multiply the array by a double but it's giving incompatible type errors. opBinary, opBinaryRight, and opOpAssign are defined. I have: struct Vector3 { public double[3] _p; nogc this(in double[] p){ switch ( p.length ) { case 0: _p[0] = _p[1] = _p[2] = 0.0; break; case 1: _p[0] = p[0]; _p[1] = _p[2] = 0.0; break; case 2: _p[0] = p[0]; _p[1] = p[1]; _p[2] = 0.0; break; default: _p[0] = p[0]; _p[1] = p[1]; _p[2] = p[2]; break; } } nogc this(in double p0, in double p1=0.0, in double p2=0.0){ _p[0] = p0; _p[1] = p1; _p[2] = p2; } nogc this(in Vector3 other){ _p[] = other._p[]; } Vector3 opBinary(string op)(in Vector3 rhs) const if (op == "+") { Vector3 result; result._p[] = this._p[] + rhs._p[]; return result; } Vector3 opBinary(string op)(in double rhs) const if (op == "*") { Vector3 result; result._p[] = this._p[] * rhs; return result; } Vector3 opBinaryRight(string op)(in double lhs) const if (op == "*") { Vector3 result; result._p[] = this._p[] * lhs; return result; } //... pure nothrow trusted nogc ref Vector3 opAssign(ref Vector3 rhs) { _p[] = rhs._p[]; return this; } pure nothrow trusted nogc ref Vector3 opAssign(Vector3 rhs) { _p[] = rhs._p[]; return this; } nogc ref Vector3 opOpAssign(string op)(in double rhs) if (op == "*") { this._p[] *= rhs; return this; } //... } unittest{ auto a = Vector3([2.0, 2.0, 0.0]); auto b = Vector3([1.0, 2.0, 1.0]); Vector3[] c = [a]; Vector3[] d = [b]; Vector3 e = a + b; //fine Vector3[] f; f[] = c[] + d[]; //fine e = 2*a; //fine e = 3.0*a; //fine f[] = 2*c[]; //Error: incompatible types for ((2) * (c[])): 'int' and 'Vector3[]' f[] = 3.0*c[]; //Error: incompatible types for ((3.00000) * (c[])): 'double' and 'Vector3[]' }
Jul 11 2015