1

Following my previous question, I'm now having trouble overriding opIndex with variadic parameters. I've tried multiple methods (even hack-ish ones) but to no avail.

The code I'm using to generate the identifier string

static string array_decl(D...)(string identifier, D dimensions)
{
    static if(dimensions.length == 0)
    {
        return identifier;
    }
    else
    {
        return array_decl(identifier  ~ "[" ~ to!(string)(dimensions[0]) ~ "]", dimensions[1..$]);
    }
}

What my opIndex override looks like:

T opIndex(D...)(D indices)
{
    mixin("return " ~ array_decl("Data", indices) ~ ";");
}

Fails with:

./inheritance.d(81): Error: tuple D is used as a type
./inheritance.d(89): Error: template instance inheritance.array_ident!(int, int, int).array_ident.array_ident!(_param_2, _param_3) error instantiating
./inheritance.d(112):        instantiated from here: array_ident!(int, int, int)
./inheritance.d(174):        instantiated from here: opIndex!(int, int, int)
./inheritance.d(112): Error: CTFE failed because of previous errors in array_ident
./inheritance.d(112): Error: argument to mixin must be a string, not ("return " ~ array_ident("Data", _param_0, _param_1, _param_2) ~ ";") of type string

The question is how (or is it possible) to implement the opIndex operator for this situation.

I think mixins are the way to go since I only have to generate a string with the format of:

type[index0][index1]...[indexN] Data

for the opIndex overload.

Community
  • 1
  • 1
  • What is teh definition of array_indent? – Adam D. Ruppe Nov 05 '15 at 16:23
  • Oops, that was a typo it was supposed to be array_decl – John Mark Gabriel Caguicla Nov 05 '15 at 16:27
  • Blargh, I don't think your approach here is going to really work. The question of implementing opIndex with variadics is really easy - you did that right, if you used indices as a normal variable and returned a normal thing, this should work. The problem is that array_decl function. What is it actually supposed to do, big picture requirements? – Adam D. Ruppe Nov 05 '15 at 16:40
  • The array_decl was supposed to be a compile-time function that generates the identifier (string) that will be used in a mixin which I will be return-ing. As for the function how can I use the indices as a normal variable? I can't just write Data[indices[0], indices[1]] without knowing how many items the indices tuple contains. – John Mark Gabriel Caguicla Nov 05 '15 at 16:49
  • 1
    but you can write `Data[indices]` and let the compiler auto-expand it to that.... :) if you need Data[i[0]][i[1]] that's different though but you could write it out by hand, there's probably only a few different lengths you will actually need anyway so like a 4-case switch could handle it. – Adam D. Ruppe Nov 05 '15 at 17:08
  • The `Data[indices]` method looked neat (didn't even know the compiler would do that), sadly I needed the `Data[i[0]][i[1]]`, I guess I'll go with the switch approach. – John Mark Gabriel Caguicla Nov 05 '15 at 17:27
  • If it is a static array btw you could also prolly index it yourself with multiplication. `int[4][4]` and `int[16]` have the same memory layout so with a bit of multiplication you could substitute the latter for the former and maybe do that too. perhaps it is time to write an answer as SO is complaining about the comments being too long. – Adam D. Ruppe Nov 05 '15 at 17:28
  • That sounds good too I'll check the solutions I have at hand, and btw you should probably write that as an answer :) – John Mark Gabriel Caguicla Nov 05 '15 at 17:41

1 Answers1

1

Apparently this is not possible as the tuple passed to opIndex is inaccessible at compile-time. A few solutions I came up with (by Adam D. Ruppe's suggestions):

1. Hard-coding Array Access

Using compile-time conditions to index the array, a bit ugly and the amount of dimensions that can be accessed depends on the amount of conditions implemented.

T opIndex(D...)(D indices)
{
    static if(indices.length == 1)
    {
        return Data[indices[0]];
    }

    static if(indices.length == 2)
    {
        return Data[indices[0]][indices[1]];
    }

    static if(indices.length == 3)
    {
        return Data[indices[0]][indices[1]][indices[2]];
    }

    static if(indices.length == 4)
    {
        return Data[indices[0]][indices[1]][indices[2]][indices[3]];
    }
}

2. Pointer(s)

The only other method was to cast the array to a pointer then use offsets. The offset is computed and is then used to index the pointer.

To be able to access the template parameters at run-time:

struct Vector_MultiDim(T, D...)
{
    enum dimensions = [D];
    static const size_t DimCount = D.length;

    ... Other members here
}

Function to compute offset (the size of each dimension must be known at run-time):

size_t GetIndex(size_t[] indices)
{
    size_t index;

    for(size_t i = 0; i < DimCount; i++)
    {
        size_t factor = 1;

        for(size_t j = i + 1; j < DimCount; j++)
        {
            factor *= dimensions[j];
        }

        index += indices[i] * factor;
    }

    return index;
}

opIndex Override:

T opIndex(D...)(D indices)
{
    T* arr = cast(T*)Data;
    return arr[GetIndex([indices])];
}