Thread overview
isBidirectionalRange fails for unknown reasons
Dec 16, 2015
Jack Stouffer
Dec 16, 2015
Jack Stouffer
Dec 16, 2015
anonymous
Dec 16, 2015
Jack Stouffer
Dec 17, 2015
Jakob Ovrum
December 16, 2015
I'm trying to add a ReferenceBidirectionalRange range type to std.internal.test.dummyrange so I can test some range code I'm writing, but I've hit a wall and I'm not sure why. For some reason, the isBidirectionalRange check fails even though back and popBack are present. Any help here would be appreciated.

the code:
============================
import std.range;

class ReferenceInputRange(T)
{
    import std.array : array;

    this(Range)(Range r) if (isInputRange!Range) { _payload = array(r); }
    final @property ref T front(){ return _payload.front; }
    final void popFront(){ _payload.popFront(); }
    final @property bool empty(){ return _payload.empty; }
    protected T[] _payload;
}

class ReferenceForwardRange(T) : ReferenceInputRange!T
{
    this(Range)(Range r) if (isInputRange!Range) { super(r); }
    final @property ReferenceForwardRange save()
    {return new ReferenceForwardRange!T( _payload); }
}

class ReferenceBidirectionalRange(T) : ReferenceForwardRange!T
{
    this(Range)(Range r) if (isInputRange!Range) { super(r); }
    final @property ref T back(){ return _payload.back; }
    final void popBack(){ _payload.popBack(); }
}

unittest
{
    static assert(isInputRange!(ReferenceInputRange!int)); // works
    static assert(isForwardRange!(ReferenceForwardRange!int)); // works
    static assert(isBidirectionalRange!(ReferenceBidirectionalRange!int)); //fails
}
December 16, 2015
On Wednesday, 16 December 2015 at 20:43:02 UTC, Jack Stouffer wrote:
> unittest
> {
>     static assert(isInputRange!(ReferenceInputRange!int)); // works
>     static assert(isForwardRange!(ReferenceForwardRange!int)); // works
>     static assert(isBidirectionalRange!(ReferenceBidirectionalRange!int)); //fails
> }

Also, this works just fine

=================
unittest
{
    auto a = new ReferenceBidirectionalRange!int([1,2]);
    a.popBack();
    a.back.writeln; // prints 1
}
December 16, 2015
On 16.12.2015 21:43, Jack Stouffer wrote:
> I'm trying to add a ReferenceBidirectionalRange range type to
> std.internal.test.dummyrange so I can test some range code I'm writing,
> but I've hit a wall and I'm not sure why. For some reason, the
> isBidirectionalRange check fails even though back and popBack are
> present. Any help here would be appreciated.
>
[...]
>
> class ReferenceForwardRange(T) : ReferenceInputRange!T
> {
>      this(Range)(Range r) if (isInputRange!Range) { super(r); }
>      final @property ReferenceForwardRange save()
>      {return new ReferenceForwardRange!T( _payload); }
> }
>
> class ReferenceBidirectionalRange(T) : ReferenceForwardRange!T
> {
>      this(Range)(Range r) if (isInputRange!Range) { super(r); }
>      final @property ref T back(){ return _payload.back; }
>      final void popBack(){ _payload.popBack(); }
> }

The `.save` primitive of forward ranges must return the very same type that the range has. But your ReferenceBidirectionalRange!T.save returns a ReferenceForwardRange!T, because it's inherited. That makes isForwardRange!(ReferenceBidirectionalRange!T) fail, and everything that depends on it.

You can override `save` in ReferenceBidirectionalRange or try something clever like using a template this parameter:

@property auto save(this This)() {return new This( _payload);}

December 16, 2015
On Wednesday, 16 December 2015 at 21:40:44 UTC, anonymous wrote:
> The `.save` primitive of forward ranges must return the very same type that the range has. But your ReferenceBidirectionalRange!T.save returns a ReferenceForwardRange!T, because it's inherited. That makes isForwardRange!(ReferenceBidirectionalRange!T) fail, and everything that depends on it.
>
> You can override `save` in ReferenceBidirectionalRange or try something clever like using a template this parameter:
>
> @property auto save(this This)() {return new This( _payload);}

Thanks! That did the trick.
December 17, 2015
On Wednesday, 16 December 2015 at 20:43:02 UTC, Jack Stouffer wrote:
> ...

You can also use return type covariance:

class ReferenceBidirectionalRange(T) : ReferenceForwardRange!T
{
    this(Range)(Range r) if (isInputRange!Range) { super(r); }
    final override @property typeof(this) save() { return new typeof(this)(_payload); }
    final @property ref T back(){ return _payload.back; }
    final void popBack(){ _payload.popBack(); }
}

ReferenceBidirectionalRange!T is a subtype of ReferenceForwardRange!T, so the override is legal.