www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Chaining opIndex

reply deed <none none.none> writes:
struct Foo {
     Bars bars;
     ...
}

struct Foos {
     Foo[] arr;
     Foo opIndex (size_t idx) { return arr[idx]; }
     ...
}

struct Bar {
     // No Car[] cars;
     ...
}

struct Bars {
     Bar[] arr;
     Bar opIndex (size_t idx) { return arr[idx]; }
     ...
}

struct Car {
     ...
}

Foos foos;
Foo foo = foos[1];                  // Works
Bar bar = foos[1].bars[2];          // Works
Car car = foos[1].bars[2].cars[3];  // Desired abstraction.

For any Bar there are some Cars, but Bar doesn't hold any Cars. 
In other words, there could be a function Car cars (Bar bar, 
size_t idx) { ... }, but that would be called with parens;

Car car = foos[i].bars[j].cars(k);

which would be inconsistent and confusing. Defining

struct Cars {
     Car opIndex (Bar bar, size_t idx) {}
}

and

struct Bar {
     Cars cars;
     ...
}

doesn't enable chaining and then would have to be used like this, 
AFAIK:

Car car = cars[foos[i].bars[j], k];

Which is out of the question. Any suggestions to achieve the 
desired abstraction in a clean manner?
May 09 2016
parent reply John Colvin <john.loughran.colvin gmail.com> writes:
On Monday, 9 May 2016 at 20:14:25 UTC, deed wrote:
 struct Foo {
     Bars bars;
     ...
 }

 struct Foos {
     Foo[] arr;
     Foo opIndex (size_t idx) { return arr[idx]; }
     ...
 }

 struct Bar {
     // No Car[] cars;
     ...
 }

 struct Bars {
     Bar[] arr;
     Bar opIndex (size_t idx) { return arr[idx]; }
     ...
 }

 struct Car {
     ...
 }

 Foos foos;
 Foo foo = foos[1];                  // Works
 Bar bar = foos[1].bars[2];          // Works
 Car car = foos[1].bars[2].cars[3];  // Desired abstraction.

 For any Bar there are some Cars, but Bar doesn't hold any Cars. 
 In other words, there could be a function Car cars (Bar bar, 
 size_t idx) { ... }, but that would be called with parens;

 Car car = foos[i].bars[j].cars(k);

 which would be inconsistent and confusing. Defining

 struct Cars {
     Car opIndex (Bar bar, size_t idx) {}
 }

 and

 struct Bar {
     Cars cars;
     ...
 }

 doesn't enable chaining and then would have to be used like 
 this, AFAIK:

 Car car = cars[foos[i].bars[j], k];

 Which is out of the question. Any suggestions to achieve the 
 desired abstraction in a clean manner?
There are lots of ways to approach this. Here's one possibility: auto cars(Bar bar) { static struct Res { Bar bar; Car opIndex(size_t i) { return /* e.g. getCar(bar, i); */ } } return Res(bar); }
May 09 2016
parent deed <none none.none> writes:
On Monday, 9 May 2016 at 22:33:37 UTC, John Colvin wrote:
 There are lots of ways to approach this. Here's one possibility:

 auto cars(Bar bar)
 {
     static struct Res
     {
         Bar bar;
         Car opIndex(size_t i)
         {
             return /* e.g. getCar(bar, i); */
         }
     }
     return Res(bar);
 }
Thanks. Didn't think of function returning struct with opIndex. Having this as opCall in a struct Cars might be a solution. What other approaches did you have in mind?
May 10 2016