1 module nxt.class_range; 2 3 import std.traits : isArray; 4 import std.range.primitives : isInputRange, ElementType; 5 6 /** Upcast all elements in `x` of type `T` to the type `U`, where `U` is a 7 * superclass of `T`. 8 * 9 * TODO move to phobos-next 10 */ 11 inout(U)[] upcastElementsTo(U, T)(scope inout(T)[] x) @trusted 12 if (is(T == class) && 13 is(U == class) 14 // TODO also check that `T` is a subclass of `U` 15 ) 16 { 17 return cast(typeof(return))x; 18 } 19 20 /// ditto 21 auto upcastElementsTo(U, R)(inout(R) x) 22 @trusted 23 if (!isArray!R && 24 is(U == class) && 25 isInputRange!R && is(ElementType!R == class) 26 // TODO also check that `ElementType!R` is a subclass of `U` 27 ) 28 { 29 import std.algorithm.iteration : map; 30 return x.map!(_ => cast(U)_); 31 } 32 33 private struct DowncastingFilterResult(Subclass, Range) 34 { 35 import core.internal.traits : Unqual; 36 37 alias R = Unqual!Range; 38 R _input; 39 private bool _primed; 40 41 alias pred = _ => cast(Subclass)_ !is null; 42 43 private void prime() 44 { 45 import std.range.primitives : empty, front, popFront; 46 if (_primed) return; 47 while (!_input.empty && !pred(_input.front)) 48 { 49 _input.popFront(); 50 } 51 _primed = true; 52 } 53 54 this(R r) 55 { 56 _input = r; 57 } 58 59 private this(R r, bool primed) 60 { 61 _input = r; 62 _primed = primed; 63 } 64 65 auto opSlice() { return this; } 66 67 import std.range.primitives : isInfinite; 68 static if (isInfinite!Range) 69 { 70 enum bool empty = false; 71 } 72 else 73 { 74 @property bool empty() 75 { 76 import std.range.primitives : empty; 77 prime(); return _input.empty; 78 } 79 } 80 81 void popFront() 82 { 83 import std.range.primitives : front, popFront, empty; 84 do 85 { 86 _input.popFront(); 87 } while (!_input.empty && !pred(_input.front)); 88 _primed = true; 89 } 90 91 @property Subclass front() 92 { 93 import std.range.primitives : front; 94 prime(); 95 assert(!empty, "Attempting to fetch the front of an empty filter."); 96 return cast(typeof(return))_input.front; 97 } 98 99 import std.range.primitives : isForwardRange; 100 static if (isForwardRange!R) 101 { 102 @property auto save() 103 { 104 import std.range.primitives : save; 105 return typeof(this)(_input.save, _primed); 106 } 107 } 108 } 109 110 /** Variant of std.algorithm.iteration : that filters out all elements of 111 * `range` that are instances of `Subclass`. 112 */ 113 template castFilter(Subclass) 114 { 115 import std.range.primitives : isInputRange, ElementType; 116 auto castFilter(Range)(Range range) 117 if (isInputRange!(Range) && 118 is(ElementType!Range == class) && 119 is(Subclass : ElementType!Range)) 120 { 121 return DowncastingFilterResult!(Subclass, Range)(range); 122 } 123 } 124 125 /// 126 @safe pure unittest 127 { 128 class X 129 { 130 this(int x) 131 { 132 this.x = x; 133 } 134 int x; 135 } 136 137 class Y : X 138 { 139 this(int x) 140 { 141 super(x); 142 } 143 } 144 145 auto y = castFilter!Y([new X(42), new Y(43)]); 146 auto yf = y.front; 147 static assert(is(typeof(yf) == Y)); 148 y.popFront(); 149 assert(y.empty); 150 }