Thread overview
array inside a class + alias this + filter -> clears the array.
Jul 07, 2021
realhet
Jul 07, 2021
Paul Backus
Jul 07, 2021
realhet
July 07, 2021

Hi,

I wanted to make a container class that exposes its elements using a simple "alias this", but getting weird errors:

I test with the std.algorithm.filter template function.

  1. when I use "alias this" on a function that returns a slice, making the internal array unreachable, filter just can't compile.
  2. when I expose the array as it is, filter deletes the array after it returns.

My goal is to make a class, which acts like an array, but also having member functions to add/remove/find its items. On top of that this class has an owner (a database table thing) too.

Example use-cases:
table.rows.add(...)
table.rows[4]
table.rows.filter!(...).map!(...)

import std.stdio, std.range, std.algorithm, std.uni, std.utf, std.conv, std.typecons, std.array, std.traits, std.exception, std.format, std.random, std.math;

class C{ //must be a class, not a struct
    int[] array;

    static if(0){
       //BAD: filter cannot deduce from an aliased function
       //     that returns a nonref array
      auto getArray(){ return array; }
      alias getArray this;
    }else{
      alias array this; //this works
    }
}

void main(){
    auto c = new C;
    c.array = [1, 2];

    void wl(){ writeln("len=", c.length); }

    //filtering the array explicitly: GOOD
    c.array.filter!"true".each!writeln; wl;

    //filtering the slice of the alias: GOOD
    c.array.filter!"true".each!writeln; wl;

    //filtering the alias: BAD -> erases the array
    c.filter!"true".each!writeln; wl;
}

Thanks in advance.

July 07, 2021

On Wednesday, 7 July 2021 at 16:20:29 UTC, realhet wrote:

>

Hi,

I wanted to make a container class that exposes its elements using a simple "alias this", but getting weird errors:

I test with the std.algorithm.filter template function.

  1. when I use "alias this" on a function that returns a slice, making the internal array unreachable, filter just can't compile.
  2. when I expose the array as it is, filter deletes the array after it returns.

My goal is to make a class, which acts like an array, but also having member functions to add/remove/find its items. On top of that this class has an owner (a database table thing) too.

In general, it is not a good idea to have your container class also function as a range, for exactly this reason. Instead, your container class should have a method that returns a range over its elements, with the range being a separate object.

The conventional way to do this is to overload opIndex:

class C
{
    private int[] array;
    this(int[] array) { this.array = array; }
    int[] opIndex() { return array; }
    // etc.
}

void main()
{
    import std.algorithm;

    auto c = new C([1, 2, 3]);
    c[].filter!"true".each!writeln;
    assert(c[].length == 3);
}
July 07, 2021

On Wednesday, 7 July 2021 at 17:10:01 UTC, Paul Backus wrote:

>

On Wednesday, 7 July 2021 at 16:20:29 UTC, realhet wrote:

>
int[] opIndex() { return array; }

Thx, I didn't know about this type of opSlice override. It works nicely.

Now I have these choices:

  • write [] everywhere to access the containers.
  • write nothing extra to access the containers but put the container operations one level higher and select the container for those based on a dummy struct.

Like going from:

db.entities.add("Entity01");
to
db.add(entity("Entity01"));   //struct entity{ string name; }

Currently I have both ways plus your solution for the range access.

Gotta experience with it to choose.

Thanks again! I will remember that common container rule from now on.