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

Random D user no at email.com
Sat May 4 15:18:58 UTC 2019


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.


More information about the Digitalmars-d-learn mailing list