Thread overview
[Issue 7753] New: Support opIndexCreate as part of index operator overloading in user-defined types
Mar 23, 2012
Dmitry Olshansky
Mar 23, 2012
Dmitry Olshansky
Mar 23, 2012
Dmitry Olshansky
March 23, 2012
http://d.puremagic.com/issues/show_bug.cgi?id=7753

           Summary: Support opIndexCreate as part of index operator
                    overloading in user-defined types
           Product: D
           Version: D2
          Platform: All
        OS/Version: All
            Status: NEW
          Severity: enhancement
          Priority: P2
         Component: DMD
        AssignedTo: nobody@puremagic.com
        ReportedBy: hsteoh@quickfur.ath.cx


--- Comment #0 from hsteoh@quickfur.ath.cx 2012-03-22 21:18:10 PDT ---
Currently, a nested indexing expression such as:

    a[b][c][d] = 0;

for a user-defined type that overloads opIndex* gets translated into:

    a.opIndex(b).opIndex(c).opIndexAssign(0,d);

However, if a[b][c] do not yet exist, this will fail. This works correctly for built-in associative arrays, because the expressions get translated into a series of calls to _aaGetX(), which creates new entries if they don't already exist. But currently, there is no way for a user-defined type to accomplish the same thing.

Suggested fix: if the expression as a whole is being assigned to with an
assignment operator, then the upper-level indexing calls should be translated
into opIndexCreate() instead of just opIndex():

    a[b][c][d] = 0;

becomes:

    a.opIndexCreate(b).opIndexCreate(c).opIndexAssign(0,d);

If opIndexCreate is not defined, then replace it with opIndex (for backward compatibility). The semantics of opIndexCreate(k) is to return the entry indexed by k if it exists, and if it doesn't, create a new entry with key k and the .init value of the value type, and return the new entry.

Preferably, this will apply to any expression that ends with a call to opIndexAssign, opIndexUnary, and opIndexOpAssign. But at the very least, this needs to work when the expression ends with opIndexAssign.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
March 23, 2012
http://d.puremagic.com/issues/show_bug.cgi?id=7753


Dmitry Olshansky <dmitry.olsh@gmail.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |dmitry.olsh@gmail.com


--- Comment #1 from Dmitry Olshansky <dmitry.olsh@gmail.com> 2012-03-23 03:28:52 PDT ---
It might be a good thing, but ...
Why not just return a proxy type upon each indexing?
The proxy type will have createIndex that will forward to others in turn.

Here a prof of concept I belive it could be generalized and polished. For simplicity sake it's for n-dim arrays:

import std.stdio, std.exception;

struct Proxy(T)
{
        T* _this;
        int idx;
        void opAssign(X)(X value){
                    debug writeln("Proxy.opAssign");
            createIndex(idx) = value;
        }
    static if(typeof(*_this).dimension >= 2)
    {

        // somewhere io expression ...a[idx][jdx]... is create all, except last
one
                auto opIndex(int jdx){
            return proxy(&_this.createIndex(idx), jdx);
        }
            //a[idx][jdx] = y; is create if non-existent
                auto opIndexAssign(X)(X val, int jdx){  //TODO: constraints!
            debug writeln("Proxy.opIndexAssign");
            _this.createIndex(idx).createIndex(jdx) = val;
        }
    }

        @property ref expr(){
                    debug writeln("Proxy.expr");
            return _this.normalIndex(idx);
        }

        alias expr this;
}

auto proxy(T)(T* x, int idx){ return Proxy!(T)(x,idx); }



struct M(size_t dim)
{
    static if(dim == 1){
        alias int Val;
    }
    else{
        alias M!(dim-1) Val;
    }
    enum dimension = dim;


    Val[] arr;


    ref createIndex(int idx){
        debug writeln("Created ", typeof(this).stringof);
        if(arr.length < idx)
            arr.length = idx+1;
        return arr[idx];
    }
        ref normalIndex(int idx){
        debug writeln("Indexed ", typeof(this).stringof);
        return arr[idx];
    }
    auto opIndex(int idx){
        return Proxy!(M)(&this, idx);
    }
        alias arr this;
}

unittest{
    M!(3) d3arr;
    d3arr[1][2] = [2, 3, 4];
    assert(d3arr[1][2][2] == 4);
    int[] x = d3arr[1][2];
    assert(d3arr[1][2].length == 3);
    assert(d3arr[1][1] == null); //inited
    //booom used before explicit =
    assert(collectException!Error(d3arr[2][2][1] + 1 == 1) !is null);
}

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
March 23, 2012
http://d.puremagic.com/issues/show_bug.cgi?id=7753



--- Comment #2 from hsteoh@quickfur.ath.cx 2012-03-23 07:36:46 PDT ---
That's a pretty neat idea. Can it be made to work with containers that contain other containers (possibly of a different type)? E.g., a linked list of arrays of AA's?

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
March 23, 2012
http://d.puremagic.com/issues/show_bug.cgi?id=7753



--- Comment #3 from Dmitry Olshansky <dmitry.olsh@gmail.com> 2012-03-23 08:17:58 PDT ---
Well, linked list is, for sure, not indexed so not a problem ;)
As for sets I don't see a problem, I can extend this idea to arbitrary set
easily. In fact it's even cleaner for sets (maps) then arrays.
If you need a headstart I can scratch up a simple version for integer sets.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
March 23, 2012
http://d.puremagic.com/issues/show_bug.cgi?id=7753



--- Comment #4 from Dmitry Olshansky <dmitry.olsh@gmail.com> 2012-03-23 08:22:00 PDT ---
Ahm. So Q was about geterogenious stuff like arrays of sets(maps) or maps of
arrays ?
I think the 2 mentioned situations cover it all, thus you can parametrize this
idea on basis of:
a) contigous container, to get item with index X you need to allocted all
elements up X. Here X is obviously can be only integer of some sort.
b)non-contigous container, to get item with index X you check/create only slot
indexed by X.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------