opIndexOpAssignOpDispatch

Quirin Schroll qs.il.paperinik at gmail.com
Thu Jun 20 10:50:19 UTC 2024


On Tuesday, 18 June 2024 at 09:37:46 UTC, Basile B. wrote:
> On Monday, 17 June 2024 at 13:40:13 UTC, Quirin Schroll wrote:
>> On Saturday, 15 June 2024 at 08:33:07 UTC, Basile B. wrote:
>>> On Saturday, 15 June 2024 at 04:58:58 UTC, monkyyy wrote:
>>>> ```d
>>>> mystruct foo;
>>>> foo[1337].isnull=true;
>>>> ```
>>>> =>
>>>> `foo.opIndexOpAssignOpDispatch!"isnull"(1337,true)`
>>>>
>>>> it fits the naming scheme!
>>>
>>> You can implement an opIndex overload that returns a struct 
>>> that itself supports opDispatch.
>>>
>>> ```d
>>> struct Foo
>>> {
>>>     struct opIndexResult
>>>     {
>>>         Foo* that;
>>>         auto opDispatch(string member, T)(T t)
>>>         {
>>>
>>>         }
>>>     }
>>>
>>>     auto opIndex(T)(T t)
>>>     {
>>>         return opIndexResult(&this);
>>>     }
>>> }
>>>
>>> void main()
>>> {
>>>     Foo foo;
>>>     foo[1337].isnull = true;
>>> }
>>> ```
>>>
>>> dont underestimate what's already possible !
>>
>> ```d
>> struct Foo
>> {
>>     struct opIndexResult(bool isRef, Arg)
>>     {
>>         Foo* that;
>>         static if (isRef)
>>         {
>>             Arg* _arg;
>>             ref Arg arg() => *_arg;
>>         }
>>         else
>>         {
>>             Arg _arg;
>>             ref Arg arg() return => _arg;
>>         }
>>         auto ref opDispatch(string member, Rhs)(Rhs rhs)
>>         {
>> 			import std.stdio;
>>             writeln("Called <something>[", arg, "].", member, 
>> " = ", rhs);
>>         }
>>     }
>>
>>     auto ref opIndex(T)(auto ref T t)
>>     {
>>         alias Result = opIndexResult!(__traits(isRef, t), T);
>>         static if (__traits(isRef, t))
>>         {
>>             return Result(&this, &t);
>>         }
>>         else
>>         {
>>             import core.lifetime : move;
>>             return Result(&this, move(t));
>>         }
>>     }
>> }
>>
>> void main() @safe
>> {
>>     Foo foo;
>>     foo[1337].isnull = true;
>> }
>> ```
>
> Nice. While the pattern was clear enough to be developped I'm 
> still slightly concerned about escaping `this`. Probably the 
> result should be set non-copiable with `@disable this(this)`.

Copies are largely irrelevant. With `-dip1000`, the escaping 
`Foo` is diagnosed.

Improved code:
```d
struct Foo
{
     int x;
     private static struct OpIndexResult(bool[] isRef, Is...)
         if (isRef.length == Is.length)
     {
         import std.meta;
         Foo* that;
         static foreach (i; 0 .. Is.length)
         static if (isRef[i])
         {
             mixin("Is[i]* _index_", i,";");
             mixin("@property ref Is[i] index_", i,"() => 
*_index_",i,";");
         }
         else
         {
             mixin("Is[i] _index_", i,";");
             mixin("@property ref Is[i] index_", i,"() => 
_index_",i,";");
         }
         template indices()
         {
             alias indices = AliasSeq!();
             static foreach (i; 0 .. Is.length)
				indices = AliasSeq!(indices, mixin("index_", i));
         }
         auto ref opDispatch(string member, Rhs...)(auto ref Rhs 
rhs)
         {
			import std.stdio;
             import std.typecons : tuple;
             writefln("Called Foo(%s)[%(%s%|, %)](%(%s%|, %))", 
that.x, tuple(indices!()), tuple(rhs));
         }
     }

     auto opIndex(Is...)(auto ref Is indices) return
     {
         import core.lifetime : move;
         enum bool[] isRef = {
             bool[] result = new bool[](Is.length);
             static foreach (i; 0 .. Is.length) result[i] = 
__traits(isRef, indices[i]);
             return result;
         }();
         alias Result = OpIndexResult!(isRef, Is);
         return mixin({
             string result = "Result(&this";
             static foreach (i, alias index; indices)
             {
                 static if (__traits(isRef, index))
                     result ~= mixin(`", &indices[`, i, `]"`);
				else
                     result ~= mixin(`", move(indices[`, i, `])"`);
             }
             return result ~ ")";
         }());
     }
}

typeof(Foo()[0,0]) global;

void main() @safe
{
     Foo foo = Foo(1);
     auto r = foo[1337, 42];
     r.isnull(true, false); // Weird, but okay

     global = foo[1337, 42]; // Error because `foo` escapes
}
```


More information about the dip.ideas mailing list