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