I had a bad time with slice-in-struct array operation forwarding/mimicing. What's the best way to do it?

Nicholas Wilson iamthewilsonator at hotmail.com
Sat May 4 15:36:51 UTC 2019


On Saturday, 4 May 2019 at 15:18:58 UTC, Random D user wrote:
> I wanted to make a 2D array like structure and support D slice 
> like operations,
> but I had surprisingly bad experience.
>
> I quickly copy pasted the example from the docs: 
> https://dlang.org/spec/operatoroverloading.html#array-ops
>
> It's something like this:
> struct Array2D(E)
> {
>     E[] impl;
>     int stride;
>     int width, height;
>
>     this(int width, int height, E[] initialData = [])
>     ref E opIndex(int i, int j)
>     Array2D opIndex(int[2] r1, int[2] r2)
>     auto opIndex(int[2] r1, int j)
>     auto opIndex(int i, int[2] r2)
>     int[2] opSlice(size_t dim)(int start, int end)
>     @property int opDollar(size_t dim : 0)()
>     @property int opDollar(size_t dim : 1)()
> }
>
> So basic indexing works fine:
> Array2D!int foo(4, 4);
> foo[0, 1] = foo[2, 3];
>
> But array copy and setting/clearing doesn't:
> int[] bar = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 
> 15 ];
> foo[] = bar[];
>
> And I get this very cryptic message:
> (6): Error: template `example.Array2D!int.Array2D.opSlice` 
> cannot deduce function from argument types `!()()`, candidates 
> are:
> (51):        `example.Array2D!int.Array2D.opSlice(ulong 
> dim)(int start, int end) if (dim >= 0 && (dim < 2))`
>
> 1. WTF `!()()` and I haven't even called anything with opSlice 
> i.e. `a .. b`?
>
> Anyway, it doesn't overload [] with opIndex(), so fine, I add 
> that.
> T[] opIndex() { return impl; }
>
> Now I get:
> foo[] = bar[]; // or foo[] = bar;
> Error: `foo[]` is not an lvalue and cannot be modified
>
> Array copying docs say:
> When the slice operator appears as the left-hand side of an 
> assignment expression, it means that the contents of the array 
> are the target of the assignment rather than a reference to the 
> array. Array copying happens when the left-hand side is a 
> slice, and the right-hand side is an array of or pointer to the 
> same type.
>
> 2.WTF I do have slice operator left of assignment.
> So I guess [] is just wonky named getter (and not an operator) 
> for a slice object and that receives the = so it's trying to 
> overwrite/set the slice object itself.
>
> Next I added a ref to the E[] opIndex():
> ref E[] opIndex() { return impl; }
>
> Now foo[] = bar[] works as expected, but then I tried
> foo[] = 0;
> and that fails:
> Error: cannot implicitly convert expression `0` of type `int` 
> to `int[]`
>
> 3. WTF. Didn't I just get reference directly to the slice and 
> array copy works, why doesn't array setting?
>
> The ugly foo[][] = 0 does work, but it's so ugly/confusing that 
> I'd rather just use a normal function.
>
> So I added:
> ref E[] opIndexAssign(E value) { impl[] = value; return impl; }
>
> And now foo[] = 0; works, but foo[0, 1] = foo[2, 3] doesn't.
>
> I get:
> Error: function `example.Array2D!int.Array2D.opIndexAssign(int 
> f)` is not callable using argument types `(int, int, int)`
> expected 1 argument(s), not 3
>
> 4. WTF. So basically adding opIndexAssign(E value) disabled ref 
> E opIndex(int i, int j). Shouldn't it consider both?
>
> I'm surprised how convoluted this is. Is this really the way 
> it's supposed to work or is there a bug or something?
>
>
> So what is the best/clear/concise/D way to do these for a 
> custom type?
>
> I was planning for:
> foo[] = bar; // Full copy
> foo[] = 0; // Full clear
> foo[0 .. 5, 1] = bar[ 0 .. 5]; // Row/Col copy
> foo[1, 0 .. 5] = 0; // Row/Col clear
> foo[0 .. 5, 2 .. 4] = bar[ 1 .. 6, 0 .. 2 ]; // Box copy
> foo[0 .. 5, 2 .. 4] = 0; // Box clear
>
> Anyway, this is not a huge deal breaker for me, I was just 
> surprised and felt like I'm missing something.
> I suppose I can manually define every case one by one and not 
> return/use any references etc.
> or use alias this to forward to impl[] (which I don't want to 
> do since I don't want to change .length for example)
> or just use normal functions and be done with it.
>
> And it's not actually just a regular array I'm making, so 
> that's why it will be mostly custom code, except the very 
> basics.

The de facto multi dimensional array type in D is mir's ndslice

https://github.com/libmir/mir-algorithm/blob/master/source/mir/ndslice/slice.d#L479


More information about the Digitalmars-d-learn mailing list