Thread overview
opIndex negative index?
Jan 21, 2021
cerjones
Jan 21, 2021
IGotD-
Jan 21, 2021
cerjones
Jan 21, 2021
IGotD-
Jan 21, 2021
IGotD-
Jan 21, 2021
cerjones
Jan 21, 2021
tsbockman
January 21, 2021
I have an iterator that steps along a 2D vector path command by command and uses opIndex to give access to the points for the current command. The issue is that there's a shared point between commands, so when the iterator is on a given command, it also needs to also allow access to the end point of the previous command. Currently the iterator juggles the indexes so that for a cubic you use 0,1,2,3, and 0 gives the previous end point. But it would simplify a few things if [-1] was for accessing the previous end point, so a cubic would allow indexes -1,0,1,2.

I'm a bit unsure if this is reasonable or not.

Thoughts?
January 21, 2021
On Thursday, 21 January 2021 at 14:00:28 UTC, cerjones wrote:
> I have an iterator that steps along a 2D vector path command by command and uses opIndex to give access to the points for the current command. The issue is that there's a shared point between commands, so when the iterator is on a given command, it also needs to also allow access to the end point of the previous command. Currently the iterator juggles the indexes so that for a cubic you use 0,1,2,3, and 0 gives the previous end point. But it would simplify a few things if [-1] was for accessing the previous end point, so a cubic would allow indexes -1,0,1,2.
>
> I'm a bit unsure if this is reasonable or not.
>
> Thoughts?

Not possible, indexes are of type size_t which is unsigned. If you want negative indexes you need to wrap arrays in your own implementation and offset 0 so that negative indexes can be used.
January 21, 2021
On Thursday, 21 January 2021 at 14:25:41 UTC, IGotD- wrote:
> On Thursday, 21 January 2021 at 14:00:28 UTC, cerjones wrote:
>
> Not possible, indexes are of type size_t which is unsigned. If you want negative indexes you need to wrap arrays in your own implementation and offset 0 so that negative indexes can be used.

import std;

struct Foo
{
    int opIndex(int idx)
    {
        return idx;
    }
}

void main()
{
    Foo foo;
    writeln(foo[-1]);
}

prints:

-1

And just for weirdness...

import std;

struct Foo
{
    string opIndex(string s)
    {
        return s;
    }
}

void main()
{
    Foo foo;
    writeln(foo["ohrealy?"]);
}

prints:

ohreally?
January 21, 2021
On Thursday, 21 January 2021 at 14:47:38 UTC, cerjones wrote:
>
> ohreally?

I thought you were talking about the built in arrays.
January 21, 2021
On Thursday, 21 January 2021 at 15:49:02 UTC, IGotD- wrote:
> On Thursday, 21 January 2021 at 14:47:38 UTC, cerjones wrote:
>>
>> ohreally?
>
> I thought you were talking about the built in arrays.

Since you create your own implementation just as you showed you are basically free to do anything. That also includes that you are free to create your own iterator as well.
January 21, 2021
On Thursday, 21 January 2021 at 15:55:02 UTC, IGotD- wrote:
> On Thursday, 21 January 2021 at 15:49:02 UTC, IGotD- wrote:
>> On Thursday, 21 January 2021 at 14:47:38 UTC, cerjones wrote:
>>>
>>> ohreally?
>>
>> I thought you were talking about the built in arrays.
>
> Since you create your own implementation just as you showed you are basically free to do anything. That also includes that you are free to create your own iterator as well.

Tbh I was just a bit uneasy about negative indexes from a design point of view, you know when something just doesnt feel right. So anyway I coded it up and decided I didnt like it anyway, just looks weird having

doSomthing(path[-1].x, path[-1].y);

so im going to stick with it as it is.

January 21, 2021
On Thursday, 21 January 2021 at 14:00:28 UTC, cerjones wrote:
> I'm a bit unsure if this is reasonable or not.
>
> Thoughts?

Signed indexes are fine, if there is a principled reason to use them. Just make sure that you are consistent by making `length`, `opDollar`, etc. use signed types, as well.

Otherwise, you're laying a trap for users of the API who may accidentally write code with mixed signed-unsigned integer operations. That's a big problem, because mixed signed-unsigned integer operations are done incorrectly by default, often with no warnings, in D and some other C-like languages.

Consistently using signed types for lengths and indexes will make the API incompatible with some Phobos (D's `std`) range code, but you should get a compile-time error message. Mixing signed and unsigned, on the other hand, is likely to result in subtly wrong code that compiles without error, but sometimes does the wrong thing.