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