Thread overview
"+=" (overloads) with custom array slices on both lhs, and rhs ??
Sep 05, 2021
james.p.leblanc
Sep 05, 2021
Paul Backus
Sep 05, 2021
james.p.leblanc
Sep 05, 2021
jfondren
Sep 06, 2021
james.p.leblanc
Sep 05, 2021
Ali Çehreli
Sep 05, 2021
james.p.leblanc
September 05, 2021

Dear D-ers,

I have constructed a custom array type that works, but is missing
correct functioning on some operator overloads.

The stripped down minimum example (module) was over 100 lines (due
overloading opAssign, etc.) Probably too long to be a good forum post.

However, a short main may explain the issue succinctly (at end).

In words: the standard D static arrays, and dynamic array can do a
"vector accumulation" using:

f += g;

We can even have slices on both lhs and rhs:

f[ 2 .. 5 ] += g[ 4 .. 7 ];

So, this should be possibly for custom array types. I have
looked at opIndexOpAssign, and learned a great deal from Michael
Parkers book on the use of opSlice, etc. with overloads. Also, I have
searched for clues in the dmd source code ... but it was not
easy to perceive how this is done.

Here is an ugly code example (without module code -- so maybe useless).

How does one overload custom types with "+=" and slices on both left and right?

Thanks in advance,
James

compilation error message appear as comments to right of offending lines

==============================================================

import std.stdio;
import myarray_mod;

void main(){

   // static arrays
   int[7] x = [ 10, 11, 12, 13, 14, 15, 16 ];
   int[7] y = [ 100, 101, 102, 103, 104, 105, 106 ];

   // custom "arrays"
   auto f = myArray( x.ptr, 7 );
   auto g = myArray( y.ptr, 6 );

   f[ 1 ] += 909;           // accumulating a scalar works fine


   f[] += 909;              // "f.opIndex() is not an lvalue and cannot be modified
   f += g;                  // f is not a scale is is a myArray
   f[ 2 .. 5 ]+=g[ 2 .. 5]; //f.opIndex(f.opSlice(2LU, 5LU ... (possible missing[])

   // for standard dynamic arrays, this works fine
   int[] a = [ 1, 2, 3, 4, 5, 6 ];
   int[] b = [ 4, 5, 6, 7, 8, 9 ];
   writeln("a: ", a);
   writeln("b: ", b);
   a[ 1 .. 4 ] += b[ 2 .. 5 ];  // even with slices on both lhs & rhs!!
   writeln("a: ", a);

   return;
}

September 05, 2021

On Sunday, 5 September 2021 at 19:43:20 UTC, james.p.leblanc wrote:

>

Dear D-ers,

I have constructed a custom array type that works, but is missing
correct functioning on some operator overloads.

[...]

>
import std.stdio;
import myarray_mod;

Please post the source code for myarray_mod so that we can reproduce the errors you're seeing.

September 05, 2021

On Sunday, 5 September 2021 at 20:38:29 UTC, Paul Backus wrote:

>

On Sunday, 5 September 2021 at 19:43:20 UTC, james.p.leblanc wrote:

>

Dear D-ers,

I have constructed a custom array type that works, but is missing
correct functioning on some operator overloads.

[...]

>
import std.stdio;
import myarray_mod;

Please post the source code for myarray_mod so that we can reproduce the errors you're seeing.

Hello Paul,

Thanks for having a look ...

James


module myArray_mod;
import std.stdio;

    struct myArray{
       int* ptr;
       size_t length;

       this( int* ptr, size_t length){
          this.ptr = ptr;
          this.length = length;
       }

       myArray opIndex(){
          return this;
       }

       int opIndex(size_t i){
          return ptr[i];
       }

       int[] opIndex( SliceInfo info ){
          return ptr[ info.start .. info.end ];
       }

       void opIndexAssign(int val, size_t i){
          ptr[i] = val;
          return;
       }

       void opIndexAssign(int val, SliceInfo info){
          auto ctr=0;
          foreach( i ; info.start .. info.end ){
             ptr[i] = val;
          }
          return;
       }


       void opIndexAssign(int[] val, SliceInfo info){
          auto ctr=0;
          foreach( i ; info.start .. info.end ){
             ptr[i] = val[ctr++];
          }
          return;
       }

       // ===========================================================

       void opIndexOpAssign(string op)(int val, int ind){
          writeln("opIndexOpAssign with INTEGER");
          if( (op == "+") || (op == "-") || (op == "*") || (op == "/") ){
             mixin(" ptr[ind] " ~ op ~ "= val;");
          }
          return;
       }

       void opIndexOpAssign(string op)(int val, SliceInfo info){
          writeln("opIndexOpAssign with SLICE");
          if( (op == "+") || (op == "-") || (op == "*") || (op == "/") ){
             foreach( i ; 0 .. length ){
                mixin(" ptr[i] " ~ op ~ "= val;");
             }
          }
          return;
       }

       void opIndexOpAssign(string op)(SliceInfo rhs, SliceInfo lhs){
          writeln("opIndexOpAssign with LHS SLICE and RHS SLICE ");
          if( (op == "+") || (op == "-") || (op == "*") || (op == "/") ){
             foreach( i ; 0 .. length ){
                mixin(" ptr[i] " ~ op ~ "= 1;");
             }
          }
          return;
       }

       myArray opBinary(string op)(myArray rhs){
          writeln("opBinary");
             if( (op == "+=") || (op == "-=") || (op == "*=") || (op == "/=") ){
                foreach( i ; 0 .. length ){
                   mixin(" ptr[i] " ~ op ~ " rhs.ptr[i];");
                }
             }
          return;
       }

       struct SliceInfo{ size_t start, end; }
       SliceInfo opSlice(size_t dim)(size_t start, size_t end){
          return SliceInfo(start, end);
       }

       void toString(scope void delegate(const(char)[]) sink) const {
          import std.format;
          sink("[");
          foreach( i ; 0 .. length ){
             formattedWrite( sink, "%s", ptr[i] );
             if( i< length-1 ) sink(", ");
          }
          sink("]");
       }
    }

September 05, 2021
On 9/5/21 12:43 PM, james.p.leblanc wrote:

> I have constructed a custom array type that works, but is missing
> correct functioning on some operator overloads.

With its old, new, and newest styles; and support for multi-dimensional use cases; operator overloading can be difficult to get right. Here is a one-dimensional case that works:

struct MyArray(T) {
  T[] elements;

  this(size_t length) {
    this.elements.length = length;
  }

  size_t opDollar() const {
    return elements.length;
  }

  auto opSliceOpAssign(string op)(T value, size_t beg, size_t end) {
    auto slice = elements[beg .. end];
    mixin (format!q{
        slice[] %s= value;
      }(op));
    return slice;
  }

  // For the entire range of elements
  auto opSliceOpAssign(string op)(T value) {
    // Dispatch to the other one
    return this.opSliceOpAssign!op(value, 0, elements.length);
  }
}

import std.stdio;
import std.format;

void main() {
  auto m = MyArray!(int)(10);

  m[2 .. 5] += 42;
  writeln(m);

  m[4 .. $] -= 100;
  writeln(m);

  m[] *= 2;
  writeln(m);
}

Ali

September 05, 2021

On Sunday, 5 September 2021 at 21:06:49 UTC, Ali Çehreli wrote:

>

On 9/5/21 12:43 PM, james.p.leblanc wrote:

>

m[4 .. $] -= 100;
writeln(m);

m[] *= 2;
writeln(m);
}

Ali

Ali,

Thanks for your example code ... I have much to learn from this
and will need to study it tomorrow when I am fresh.

I hope that I will see how to extend this so that it can do
slices on both the left side and right side ... for example:


m[ 4 .. 8 ] += q[ 7 .. 11 ];

Best Regards,
James

September 05, 2021

On Sunday, 5 September 2021 at 20:49:08 UTC, james.p.leblanc wrote:

>

On Sunday, 5 September 2021 at 20:38:29 UTC, Paul Backus wrote:

>

Please post the source code for myarray_mod so that we can reproduce the errors you're seeing.

Hello Paul,

Thanks for having a look ...

James

Here's a reduction of your myArray.d that works with your unchanged usage code:

module myArray;
import std.stdio;

struct myArray {
    int* ptr;
    size_t length;

    myArray opIndex() {
        return this;
    }

    int opIndex(size_t i) {
        return ptr[i];
    }

    void opIndexOpAssign(string op)(int val, int ind) {
        if ((op == "+") || (op == "-") || (op == "*") || (op == "/")) {
            mixin(" ptr[ind] " ~ op ~ "= val;");
        }
    }

    myArray opSlice(size_t start, size_t end) {
        return myArray(&ptr[start], end - start);
    }

    void opOpAssign(string op)(int rhs) if (op == "+") {
        foreach (i; 0 .. length) {
            mixin("ptr[i] " ~ op ~ "= i;");
        }
    }

    void opOpAssign(string op)(myArray rhs) if (op == "+") {
        foreach (i; 0 .. length) {
            mixin("ptr[i] " ~ op ~ "= rhs.ptr[i];");
        }
    }
}
September 06, 2021

On Sunday, 5 September 2021 at 21:25:06 UTC, jfondren wrote:

>

On Sunday, 5 September 2021 at 20:49:08 UTC, james.p.leblanc wrote:

>

Here's a reduction of your myArray.d that works with your unchanged usage code:

module myArray;
import std.stdio;

    void opOpAssign(string op)(myArray rhs) if (op == "+") {
        foreach (i; 0 .. length) {
            mixin("ptr[i] " ~ op ~ "= rhs.ptr[i];");
        }
    }

Wow, these quick and helpful replies are gratefully received!

They have helped me learn many new aspects of D (especially the overloading!)

Serious thanks to all.

Best Regards,
James