Jump to page: 1 2
Thread overview
The difference between T[] opIndex() and T[] opSlice()
Oct 01, 2023
Salih Dincer
Oct 01, 2023
Salih Dincer
Oct 02, 2023
Jonathan M Davis
Oct 02, 2023
Salih Dincer
Oct 02, 2023
Paul Backus
Oct 02, 2023
Salih Dincer
Oct 02, 2023
Paul Backus
Oct 02, 2023
Salih Dincer
Oct 03, 2023
Paul Backus
Oct 03, 2023
Paul Backus
Oct 03, 2023
Imperatorn
Oct 03, 2023
Salih Dincer
Oct 03, 2023
Paul Backus
Oct 05, 2023
Gaurav Negi
Oct 05, 2023
Salih Dincer
October 01, 2023

Hi,

What is the difference between T[] opIndex() and T[] opSlice(), which haven't parameters?

struct S(T)
{
    T[] arr;
    
    T[] opIndex() => arr[];/*
    T[] opSlice() => arr;//*/
}

alias Type = int;
void main()
{
    auto s = S!Type([1,2,3]);
    auto arr = s[]; // calls s.opIndex()
    
    assert(arr == [1,2,3]);
    assert(is(typeof(arr): Type[]));
}

Also, is it correct to use [] when returning?

Thanks...

October 01, 2023

On Sunday, 1 October 2023 at 17:41:08 UTC, Salih Dincer wrote:

>

Also, is it correct to use [] when returning?

Thanks...

Here, the opIndex() is proposed and an example of parameterized the opSlice() is given for multidimensional arrays. Like there's no difference, huh?

Puff :)

October 01, 2023
On Sunday, October 1, 2023 11:51:17 AM MDT Salih Dincer via Digitalmars-d- learn wrote:
> On Sunday, 1 October 2023 at 17:41:08 UTC, Salih Dincer wrote:
> > Also, is it correct to use [] when returning?
> >
> > Thanks...
>
> [Here](https://dlang.org/spec/operatoroverloading.html#slice),
> the opIndex() is proposed and an example of parameterized the
> opSlice() is given for multidimensional arrays.  Like there's no
> difference, huh?
>
> Puff :)

I suspect that the only people who really understand the full mess with opIndex and opSlice at this point are the folks who have done a bunch with multi-dimensional containers, which fortunately, I haven't had to deal with any time recently, so I'm not well-versed on all of the nitty-gritty details.

The situation used to be a bit clearer, but folks wanted better support for multi-dimensional containers, so some changes were made. The result is that if you're dealing with multiple arguments, the difference in which is called should come down to whether you're using the slice operator between indices - .. - or whether you're using commas (though since you can mix and match to an extent with multi-dimensional containers, it's still pretty confusing IMHO). Either way, when you have no arguments, the situation is certainly more confusing than it used to be.

Previously, you would have always used opSlice with no parameters for something like returning the full slice of a container, but I think that it's now possible to do exactly the same thing with opIndex (confusing as that may be), because that made some sense when writing a variadic opIndex.

For most code, you'd just write an opIndex with a single parameter for indexing an element, opSlice with two parameters for slicing the range or container, and then either opIndex or opSlice with no parameters to return a slice of the entire container (in which case, personally, I'd use opSlice, because semantically, that's what you're doing, but either should work IIRC).

- Jonathan M Davis



October 01, 2023

On 10/1/23 1:41 PM, Salih Dincer wrote:

>

Hi,

What is the difference between T[] opIndex() and T[] opSlice(), which haven't parameters?

None. It used to be that opSlice was the only way, and the mechanisms opSlice uses are still valid.

-Steve

October 02, 2023

On Monday, 2 October 2023 at 02:01:34 UTC, Jonathan M Davis wrote:

>

For most code, you'd just write an opIndex with a single parameter for indexing an element, opSlice with two parameters for slicing the range or container, and then either opIndex or opSlice with no parameters to return a slice of the entire container (in which case, personally, I'd use opSlice, because semantically, that's what you're doing, but either should work IIRC).

Overloading has nothing to do with indexing, so I'll use opSlice.

import std.stdio;
import std.range;

struct Matrix(T)
{
  private T[][] elements;
  size_t length;
  T* ptr;

  this(size_t length)
  {
    this.length = length * length;
    size_t m = T.sizeof * this.length;
    ubyte[] arr = new ubyte[](m);

    ptr = cast(T*)arr.ptr;
    m /= length;

    foreach(i; 0 .. length)
    {
      size_t n = i * m;
      elements ~= cast(T[])arr[n .. n + m];
    }
  }

  ref T opIndex(size_t i)
  in(i < length)
    => ptr[i];

  auto opDollar() => length;
  auto opSliceAssign(T value, size_t a, size_t b)
  in(a <= length && b <= length)
    => ptr[a..b] = value;

  auto opSlice() => elements;
  auto opSliceAssign(T value)
  {
    foreach(i; 0 .. length)
    {
      ptr[i] = value;
    }
  }
}

void main()
{
  auto arr = Matrix!double(3);
  size_t n;

  foreach(value; iota(0.1, 1, 0.1))
    arr[n++] = value;
  
  arr[].writefln!"%-(%-(%s %)\n%)\n";
  
  arr[0..$/2] = 0;   // reset a slice

  arr[].writefln!"%-(%-(%s %)\n%)\n";

  arr[] = 0;             // reset all
  
  arr[].writefln!"%-(%-(%s %)\n%)\n";
  
  arr[6..9] = 1;       // set a slice
  arr[].writefln!"%-(%-(%s %)\n%)\n";
}

SDB@79

October 02, 2023

On Sunday, 1 October 2023 at 17:41:08 UTC, Salih Dincer wrote:

>

Hi,

What is the difference between T[] opIndex() and T[] opSlice(), which haven't parameters?

T[] opSlice() is the D1 version and exists only for backwards compatibility. You should use T[] opIndex() in new code.

October 02, 2023

On Monday, 2 October 2023 at 16:05:39 UTC, Paul Backus wrote:

>

T[] opSlice() is the D1 version and exists only for backwards compatibility. You should use T[] opIndex() in new code.

Forgive me for asking again, I think opSliceAssign(T value) has also been improved, right?

   // ...

   auto opSlice()
   {
     return elements[];
   }

   auto opSliceAssign(T value)
   {
     foreach(i; 0 .. length)
     {
       ptr[i] = value;
     }
   }
 }

In an old version (for example, v2.0.83), the code you implemented in the places where Slice is written above works as desired. In the most current versions, the parameterized opIndexAssign(T value) gives the error:

>

onlineapp.d(51): Error: function onlineapp.Matrix!double.Matrix.opIndexAssign(double value) is not callable using argument types (double, ulong)
onlineapp.d(51): expected 1 argument(s), not 2

Source: https://run.dlang.io/is/TPAg5m

Thanks...

SDB@79

October 02, 2023

On Monday, 2 October 2023 at 20:34:11 UTC, Salih Dincer wrote:

>

In an old version (for example, v2.0.83), the code you implemented in the places where Slice is written above works as desired. In the most current versions, the parameterized opIndexAssign(T value) gives the error:

>

onlineapp.d(51): Error: function onlineapp.Matrix!double.Matrix.opIndexAssign(double value) is not callable using argument types (double, ulong)
onlineapp.d(51): expected 1 argument(s), not 2

Source: https://run.dlang.io/is/TPAg5m

I don't know what's wrong in your example but this works for me:

struct S
{
    void opIndexAssign(int value)
    {
        import std.stdio;
        writeln("assigned ", value);
    }
}

void main()
{
    S s;
    s[] = 7;
}
October 02, 2023
On Monday, 2 October 2023 at 20:42:14 UTC, Paul Backus wrote:
>
> I don't know what's wrong in your example but this works for me:
>

I found the reason for the error:

https://forum.dlang.org/thread/vckvftkdzcrnikuduuqp@forum.dlang.org

SDB@79
October 03, 2023

On Monday, 2 October 2023 at 20:42:14 UTC, Paul Backus wrote:

>

On Monday, 2 October 2023 at 20:34:11 UTC, Salih Dincer wrote:

>

In an old version (for example, v2.0.83), the code you implemented in the places where Slice is written above works as desired. In the most current versions, the parameterized opIndexAssign(T value) gives the error:

>

onlineapp.d(51): Error: function onlineapp.Matrix!double.Matrix.opIndexAssign(double value) is not callable using argument types (double, ulong)
onlineapp.d(51): expected 1 argument(s), not 2

Source: https://run.dlang.io/is/TPAg5m

I don't know what's wrong in your example but this works for me:

struct S
{
    void opIndexAssign(int value)
    {
        import std.stdio;
        writeln("assigned ", value);
    }
}

void main()
{
    S s;
    s[] = 7;
}

So in the example linked by Salih, the opIndex returns a ref, which is a valid mechanism to properly do a[0] = val;. However, since opIndexAssign exists, the compiler expects that to be used instead.

Essentially, by naming the slice assign the same operator as index assign, you have eliminated the possibility for ref assignment via indexing.

Now, you can define a further opIndexAssign(T val, size_t idx). However, now you lose capabilities like a[0]++, which I don't think has a possibility of implementing using an opIndex operator, and it would be pretty ugly if you had to.

This seems like a design flaw in the opIndex overloading changes. I would stick with opSliceAssign if faced with this problem (glad it still works!)

It could also be considered a bug but I don't know the overload implications.

-Steve

« First   ‹ Prev
1 2