Getting a range over a const Container

Francisco Soulignac fsoulign at dc.uba.ar
Wed Jul 18 21:39:26 PDT 2012


Hi all,

it's been a while since this question, and I don't know how to solve
it either.  The following code passes all the test using the last
version of dmd (2.059).

import std.container, std.algorithm;

//non const case
void assertequal(T)(SList!(T) l, int[] r) {
    assert(equal(l[], r));
}

//const case
void const_assertequal(T)(const SList!(T) l, int[] r) {
    assert(equal(l[], r));
}

unittest{
    SList!(int) l;
    l.insertFront(2);
    l.insertFront(1);
    assertequal (l,   [1,2]);
    assert(!__traits(compiles, const_assertequal(l, [1,2])));
}

The conflict with the last assertion is that opSplice can't be
applied to const.  So, I looked at the container module, and made a
minimal example of a list myself (emulating SList).

struct List {
    struct Node {
        Node* next;
        int val;
    }

    private Node* first;

    struct Range {
//sorry about "actual", it should have been current
        private Node* actual;
        this(Node* first) {actual = first;}
        @property front() {return actual.val;}
        @property empty() const {return !actual;}
        void popFront() {assert(!empty); actual = actual.next;}
        Range save() {return this;}
    }
    unittest{static assert(isForwardRange!(Range));}

    void add(int v) {
        Node * next = first;
        first = new Node;
        first.val = v;
        first.next = next;
    }

    Range opSlice() {
        return Range(first);
    }

}

void assertequal(L : List)(L l, int[] r) {
    assert(equal(l[], r));
}

void assertequal_const(L : List)(L l, int[] r) {
    assert(equal(l[], r));
}

unittest{
    List l;
    l.add(2);
    l.add(1);
    assert(equal(l[], [1,2]));
    assertequal (l,   [1,2]);
    assert(!__traits(compiles, const_assertequal(l, [1,2])));
}

As far as I know, the problem comes with the transitivity of const.
In assertequal_const, l.first has type const(Node*), thus it can't
be converted to Node* in Range's constructor.  I can't manage to
find a workaround here, because l.first will always have type
const(Node*), but for traversing the list I require to copy l.first
into some node, say actual, whose type is Node* so that I can move
it doing actual = actual.next.  I would be happy to do

actual = cast(Node*)(l.first)
actual = actual.next;

but that code is suppose to be undefined, isn't it?
(http://dlang.org/const3.html : Removing Immutable With A Cast).

So, my question is how can I (correctly) traverse a const SList,
const DList, etc?

Best,
Francisco.
PS: sorry for the long mail.


More information about the Digitalmars-d-learn mailing list