On 09/04/2014 12:19 PM, "Marc Schütz" <schue...@gmx.net>" wrote:
On Thursday, 4 September 2014 at 19:12:27 UTC, Ali Çehreli wrote:
The documentation says "To overload a[], simply define opIndex with no
parameters":

  http://dlang.org/operatoroverloading.html#Slice

And it works with some uses of a[]. However, opSlice() seems to be
needed to actually use the returned slice further.

Note that opSlice() also seems to be for backward compatibility: "For
backward compatibility, a[] and a[i..j] can also be overloaded by
implementing opSlice() with no arguments and opSlice(i, j) with two
arguments, respectively. This only applies for one-dimensional
slicing, and dates from when D did not have full support for
multidimensional arrays. This usage of opSlice is discouraged."

How can I achieve the last line in main() without needing opSlice()? I
am trying to let the Slice type handle opAssign() and friends.


--8<-- snip --8<--

    // This requires opSlice.
    c[] = 42;
}

Have you tried opIndexAssign()?

That works and kind of makes sense.

My main confusion stems from the fact that the new templated opSlice() does not have its own section on the documentation page, other than appearing in examples of other operators.

What I did not like about opIndexAssign() at first is the fact that it puts the Collection in the middle and requires an explicit dispatch by the programmer to Slice. In contrast, the legacy way (which still works) bypasses Collection and goes directly to Slice.

However, the legacy way supports only one dimension.

Also, the new way is necessary as the same function (namely, opIndexAssign) takes care of both indexing and slicing. This is from the change log:

    void opIndexAssign(A...)(E val, A indices)
    {
        assert(A.length == payload.length);

        foreach (dim, x; indices)
        {
            static if (is(typeof(x) : size_t))
            {
                // this[..., x, ...]
                payload[dim][x] = val;
            }
            else
            {
                // this[..., x[0] .. x[1], ...]
                payload[dim][x[0] .. x[1]] = val;
            }
        }
    }

So, the following is my current solution. Note that opIndex() seems still be necessary. Is it possible to replace it with an opSlice overload?

import std.stdio;

struct Collection
{
    int[] elements;

    /* Handles the slice operations */
    struct Slice
    {
        int[] slice;

        Slice opAssign(int value)
        {
            writefln("Slice.opAssign(int)");
            slice[] = value;
            return this;
        }
    }

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

    // CHALLENGE: Can you replace this with an opSlice overload?
    Slice opIndex()
    {
        writefln("opIndex()");
        return Slice(elements);
    }

    Slice opSlice(size_t dim, A...)(A args)
    {
        writefln("opSlice!%s(%(%s, %))", dim, [ args ]);
        return Slice(elements[args[0] .. args[1]]);
    }

    Collection opIndexAssign(A...)(int value, A indexes)
    {
        writefln("opIndexAssign(%s, %(%s, %))", value, [ indexes ]);

        foreach (dim, x; indexes)
        {
            static if (is(typeof(x) : size_t))
            {
                elements[x] = value;

            } else static if (is(typeof(x) : Slice)) {
                x = value;

            } else {
                static assert(false);
            }
        }

        return this;
    }
}

void main()
{
    auto c = Collection([ 0, 1, 2, 3]);

    writeln("\n--- c[] ---");
    c[];

    writeln("\n--- c[1 .. $-1] = 42 ---");
    c[1 .. $-1] = 42;

    writeln("\n--- c[3] = 100 ---");
    c[3] = 100;

    assert(c.elements == [ 0, 42, 42, 100 ]);
}

The output:

--- c[] ---
opIndex()

--- c[1 .. $-1] = 42 ---
opDollar()
opSlice!0(1, 3)
opIndexAssign(42, Slice([1, 2]))
Slice.opAssign(int)

--- c[3] = 100 ---
opIndexAssign(100, 3)

Ali

Reply via email to