1 module typecons_ex; 2 3 // TODO Add to Phobos and refer to http://forum.dlang.org/thread/lzyqywovlmdseqgqfvun@forum.dlang.org#post-ibvkvjwexdafpgtsamut:40forum.dlang.org 4 // TODO Better with?: 5 /* inout(Nullable!T) nullable(T)(inout T a) */ 6 /* { */ 7 /* return typeof(return)(a); */ 8 /* } */ 9 /* inout(Nullable!(T, nullValue)) nullable(alias nullValue, T)(inout T value) */ 10 /* if (is (typeof(nullValue) == T)) */ 11 /* { */ 12 /* return typeof(return)(value); */ 13 /* } */ 14 15 public import ties; 16 17 import std.typecons: Nullable, NullableRef; 18 19 /** Instantiator for $(D Nullable). 20 */ 21 auto nullable(T)(T a) 22 { 23 return Nullable!T(a); 24 } 25 unittest 26 { 27 auto x = 42.5.nullable; 28 assert(is(typeof(x) == Nullable!double)); 29 } 30 31 /** Instantiator for $(D Nullable). 32 */ 33 auto nullable(alias nullValue, T)(T value) 34 if (is (typeof(nullValue) == T)) 35 { 36 return Nullable!(T, nullValue)(value); 37 } 38 unittest 39 { 40 auto x = 3.nullable!(int.max); 41 assert(is (typeof(x) == Nullable!(int, int.max))); 42 } 43 44 /** Instantiator for $(D NullableRef). 45 */ 46 auto nullableRef(T)(T* a) @safe pure nothrow 47 { 48 return NullableRef!T(a); 49 } 50 unittest 51 { 52 auto x = 42.5; 53 auto xr = nullableRef(&x); 54 assert(!xr.isNull); 55 xr.nullify; 56 assert(xr.isNull); 57 } 58 59 /** See also: http://forum.dlang.org/thread/jwdbjlobbilowlnpdzzo@forum.dlang.org 60 */ 61 template New(T) if (is(T == class)) 62 { 63 T New(Args...) (Args args) { 64 return new T(args); 65 } 66 } 67 68 import std.traits: isArray, isUnsigned, isInstanceOf, isSomeString; 69 import std.range.primitives: hasSlicing; 70 71 /** Check if $(D T) is castable to $(D U). 72 */ 73 enum isCastableTo(T, U) = __traits(compiles, { T i = 0; cast(U)i; }); 74 75 enum isIndex(I) = (is(I == enum) || 76 isUnsigned!I || // TODO should we allow isUnsigned here? 77 isCastableTo!(I, size_t)); 78 79 /** Check if $(D R) is indexable by $(D I). */ 80 enum isIndexableBy(R, I) = (isArray!R && // TODO generalize to RandomAccessContainers. Ask on forum for hasIndexing!R. 81 isIndex!I); 82 83 unittest 84 { 85 static assert(isIndexableBy!(int[3], ubyte)); 86 } 87 88 /** 89 Check if $(D R) is indexable by $(D I). 90 */ 91 enum isIndexableBy(R, alias I) = (isArray!R && // TODO generalize to RandomAccessContainers. Ask on forum for hasIndexing!R. 92 (isSomeString!(typeof(I)))); 93 94 unittest 95 { 96 static assert(isIndexableBy!(int[], "I")); 97 } 98 99 mixin template genOps(T) 100 { 101 auto ref opIndex(T i) inout 102 { 103 assert(cast(size_t)i < _r.length, "Range violation with index " ~ T.stringof); 104 return _r[cast(size_t)i]; 105 } 106 auto ref opIndexAssign(V)(V value, T i) 107 { 108 assert(cast(size_t)i < _r.length, "Range violation with index " ~ T.stringof); 109 return _r[cast(size_t)i] = value; 110 } 111 112 static if (hasSlicing!R) 113 { 114 auto ref opSlice(T i, T j) inout { return _r[cast(size_t)i .. 115 cast(size_t)j]; } 116 auto ref opSliceAssign(V)(V value, T i, T j) { return _r[cast(size_t)i .. 117 cast(size_t)j] = value; } 118 } 119 } 120 121 /** Wrapper for $(D R) with Type-Safe $(D I)-Indexing. 122 See also: http://forum.dlang.org/thread/gayfjaslyairnzrygbvh@forum.dlang.org#post-gayfjaslyairnzrygbvh:40forum.dlang.org 123 124 TODO Merge with https://github.com/rcorre/enumap 125 126 TODO Use std.range.indexed when I is an enum with non-contigious 127 enumerators. Perhaps use among aswell. 128 129 TODO Rename to something more concise such as [Bb]y. 130 131 TODO Allow $(D I) to be a string and if so derive $(D Index) to be that string. 132 133 TODO Support R being a static array: 134 - If I is an enum its number of elements should match R.length 135 */ 136 struct IndexedBy(R, I) 137 if (isIndexableBy!(R, I)) 138 { 139 alias Index = I; /// indexing type 140 mixin genOps!I; 141 R _r; 142 alias _r this; // TODO Use opDispatch instead; to override only opSlice and opIndex 143 } 144 145 /** Instantiator for $(D IndexedBy). 146 */ 147 auto indexedBy(I, R)(R range) 148 if (isIndexableBy!(R, I)) 149 { 150 return IndexedBy!(R, I)(range); 151 } 152 153 struct IndexedBy(R, string I_ = "Index") 154 if (isArray!R && 155 I_ != "I_") // prevent name lookup failure 156 { 157 mixin(q{ struct } ~ I_ ~ 158 q{ { 159 alias T = size_t; 160 this(T ix) { this._ix = ix; } 161 T opCast(U : T)() const { return _ix; } 162 private T _ix = 0; 163 } 164 }); 165 mixin genOps!(mixin(I_)); 166 R _r; 167 alias _r this; // TODO Use opDispatch instead; to override only opSlice and opIndex 168 } 169 170 /** Instantiator for $(D IndexedBy). 171 */ 172 auto indexedBy(string I, R)(R range) 173 if (isArray!R && 174 I != "I_") // prevent name lookup failure 175 { 176 return IndexedBy!(R, I)(range); 177 } 178 179 /** Instantiator for $(D IndexedBy). 180 */ 181 auto indexed(R)(R range) 182 if (isArray!R) 183 { 184 return IndexedBy!(R)(range); 185 } 186 187 @safe pure nothrow unittest 188 { 189 int[3] x = [1, 2, 3]; 190 191 // sample index 192 struct Index(T = size_t) 193 if (isUnsigned!T) 194 { 195 this(T i) { this._i = i; } 196 T opCast(U : T)() const { return _i; } 197 private T _i = 0; 198 } 199 alias J = Index!size_t; 200 201 enum E { e0, e1, e2 } 202 203 with (E) 204 { 205 auto xb = x.indexedBy!ubyte; 206 auto xi = x.indexedBy!uint; 207 auto xj = x.indexedBy!J; 208 auto xe = x.indexedBy!E; 209 auto xf = x.indexed; 210 211 auto xs = x.indexedBy!"I"; 212 alias XS = typeof(xs); 213 XS xs_; 214 215 // indexing with correct type 216 xb[ 0 ] = 11; assert(xb[ 0 ] == 11); 217 xi[ 0 ] = 11; assert(xi[ 0 ] == 11); 218 xj[J(0)] = 11; assert(xj[J(0)] == 11); 219 xe[ e0 ] = 11; assert(xe[ e0 ] == 11); 220 xs[XS.I(0)] = 11; assert(xs[XS.I(0)] == 11); 221 xs_[XS.I(0)] = 11; assert(xs_[XS.I(0)] == 11); 222 223 // indexing with wrong type 224 static assert(!__traits(compiles, { xb[J(0)] = 11; })); 225 static assert(!__traits(compiles, { xi[J(0)] = 11; })); 226 static assert(!__traits(compiles, { xj[ 0 ] = 11; })); 227 static assert(!__traits(compiles, { xe[ 0 ] = 11; })); 228 static assert(!__traits(compiles, { xs[ 0 ] = 11; })); 229 static assert(!__traits(compiles, { xs_[ 0 ] = 11; })); 230 231 import std.algorithm.comparison: equal; 232 import std.algorithm.iteration: filter; 233 234 assert(equal(xb[].filter!(a => a < 11), [2, 3])); 235 assert(equal(xi[].filter!(a => a < 11), [2, 3])); 236 assert(equal(xj[].filter!(a => a < 11), [2, 3])); 237 assert(equal(xe[].filter!(a => a < 11), [2, 3])); 238 assert(equal(xs[].filter!(a => a < 11), [2, 3])); 239 } 240 } 241 242 @safe pure nothrow unittest 243 { 244 auto x = [1, 2, 3]; 245 246 // sample index 247 struct Index(T = size_t) 248 if (isUnsigned!T) 249 { 250 this(T ix) { this._ix = ix; } 251 T opCast(U : T)() const { return _ix; } 252 private T _ix = 0; 253 } 254 alias J = Index!size_t; 255 256 enum E { e0, e1, e2 } 257 258 with (E) 259 { 260 auto xb = x.indexedBy!ubyte; 261 auto xi = x.indexedBy!uint; 262 auto xj = x.indexedBy!J; 263 auto xe = x.indexedBy!E; 264 265 // indexing with correct type 266 xb[ 0 ] = 11; assert(xb[ 0 ] == 11); 267 xi[ 0 ] = 11; assert(xi[ 0 ] == 11); 268 xj[J(0)] = 11; assert(xj[J(0)] == 11); 269 xe[ e0 ] = 11; assert(xe[ e0 ] == 11); 270 271 // slicing with correct type 272 xb[ 0 .. 1 ] = 12; assert(xb[ 0 .. 1 ] == [12]); 273 xi[ 0 .. 1 ] = 12; assert(xi[ 0 .. 1 ] == [12]); 274 xj[J(0) .. J(1)] = 12; assert(xj[J(0) .. J(1)] == [12]); 275 xe[ e0 .. e1 ] = 12; assert(xe[ e0 .. e1 ] == [12]); 276 277 // indexing with wrong type 278 static assert(!__traits(compiles, { xb[J(0)] = 11; })); 279 static assert(!__traits(compiles, { xi[J(0)] = 11; })); 280 static assert(!__traits(compiles, { xj[ 0 ] = 11; })); 281 static assert(!__traits(compiles, { xe[ 0 ] = 11; })); 282 283 // slicing with wrong type 284 static assert(!__traits(compiles, { xb[J(0) .. J(0)] = 11; })); 285 static assert(!__traits(compiles, { xi[J(0) .. J(0)] = 11; })); 286 static assert(!__traits(compiles, { xj[ 0 .. 0 ] = 11; })); 287 static assert(!__traits(compiles, { xe[ 0 .. 0 ] = 11; })); 288 289 import std.algorithm.comparison: equal; 290 import std.algorithm.iteration: filter; 291 292 assert(equal(xb.filter!(a => a < 11), [2, 3])); 293 assert(equal(xi.filter!(a => a < 11), [2, 3])); 294 assert(equal(xj.filter!(a => a < 11), [2, 3])); 295 assert(equal(xe.filter!(a => a < 11), [2, 3])); 296 } 297 } 298 299 @safe pure nothrow unittest 300 { 301 auto x = [1, 2, 3]; 302 struct I(T = size_t) 303 { 304 this(T ix) { this._ix = ix; } 305 T opCast(U : T)() const { return _ix; } 306 private T _ix = 0; 307 } 308 alias J = I!size_t; 309 auto xj = x.indexedBy!J; 310 } 311 312 @safe pure nothrow unittest 313 { 314 auto x = [1, 2, 3]; 315 struct I(T = size_t) 316 { 317 this(T ix) { this._ix = ix; } 318 private T _ix = 0; 319 } 320 alias J = I!size_t; 321 static assert(!__traits(compiles, { auto xj = x.indexedBy!J; })); 322 } 323 324 @safe pure nothrow unittest 325 { 326 auto x = [1, 2, 3]; 327 import bound: Bound; 328 alias B = Bound!(ubyte, 0, 2); 329 B b; 330 auto c = cast(size_t)b; 331 auto y = x.indexedBy!B; 332 } 333 334 /** TODO shorter name */ 335 enum StaticArrayOfElementTypeIndexedBy(E, I) = IndexedBy!(E[I.elementCountOf!E], I);