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