1 /// General-purpose wrapper/construct types for interacting with types. 2 module tern.typecons.common; 3 4 import tern.memory : memcpy; 5 import tern.traits; 6 import tern.object : qdup, factory, loadLength; 7 import std.conv; 8 9 /// Implements all functions of an abstract class with an default/empty function. 10 public class BlackHole(T) 11 if (isAbstractClass!T) 12 { 13 mixin(fullIdentifier!T~" val; 14 alias val this;"); 15 static foreach (func; Functions!T) 16 { 17 static if (isAbstractFunction!(getChild!(T, func))) 18 { 19 static if (isNoReturn!(getChild!(T, func))) 20 mixin(FunctionSignature!(getChild!(T, func))~" { return "~fullIdentifier!(ReturnType!(getChild!(T, func)))~".init; }"); 21 else 22 mixin(FunctionSignature!(getChild!(T, func))~" { }"); 23 } 24 } 25 } 26 27 /// Implements all functions of an abstract class with an assert trap. 28 public class WhiteHole(T) 29 if (isAbstractClass!T) 30 { 31 mixin(fullIdentifier!T~" val; 32 alias val this;"); 33 static foreach (func; Functions!T[0..$-5]) 34 { 35 static if (isAbstractFunction!(getChild!(T, func))) 36 mixin(FunctionSignature!(getChild!(T, func))~" { assert(0); }"); 37 } 38 } 39 40 /** 41 * Wraps `T` to allow it to be defined as null. 42 * No, this is not actually an optional, it is literally backed by a pointer and thus *actually* nullable. 43 * 44 * Remarks: 45 * - `opOpAssign` is not supported for fields of `T`. 46 * - const Nullable(T) is not supported, but shared Nullable(T) is. 47 */ 48 private alias NULL = typeof(null); 49 public struct Nullable(T) 50 { 51 T value; 52 alias value this; 53 54 public: 55 final: 56 T* ptr; 57 58 this(T val) 59 { 60 value = val; 61 ptr = &value; 62 } 63 64 this(NULL val) 65 { 66 67 } 68 69 auto opAssign(A)(A ahs) 70 { 71 value = ahs; 72 ptr = &value; 73 return this; 74 } 75 76 auto opAssign(A)(A ahs) shared 77 { 78 value = ahs; 79 ptr = &value; 80 return this; 81 } 82 83 A opCast(A)() const 84 { 85 return Nullable!A(cast(A)value); 86 } 87 88 A opCast(A)() const shared 89 { 90 return Nullable!A(cast(A)value); 91 } 92 93 auto opUnary(string op)() 94 { 95 static if (op.length == 2) 96 ptr = &value; 97 98 if (ptr == null) 99 throw new Throwable("Null object reference T.T"); 100 101 return mixin("Nullable!T(cast(T)("~op~"value))"); 102 } 103 104 auto opUnary(string op)() shared 105 { 106 static if (op.length == 2) 107 ptr = &value; 108 109 if (ptr == null) 110 throw new Throwable("Null object reference T.T"); 111 112 return mixin("Nullable!T(cast(T)("~op~"value))"); 113 } 114 115 auto opEquals(A)(A ahs) const 116 { 117 static if (is(A == NULL)) 118 return ptr == null; 119 else 120 return value == ahs; 121 } 122 123 auto opEquals(A)(A ahs) const shared 124 { 125 static if (is(A == NULL)) 126 return ptr == null; 127 else 128 return value == ahs; 129 } 130 131 int opCmp(A)(A ahs) const 132 { 133 if (ptr == null) 134 throw new Throwable("Null object reference T.T"); 135 136 static if (isScalar!T) 137 return cast(int)(value - ahs); 138 else 139 return mixin("value.opCmp(ahs)"); 140 } 141 142 int opCmp(A)(A ahs) const shared 143 { 144 if (ptr == null) 145 throw new Throwable("Null object reference T.T"); 146 147 static if (isScalar!T) 148 return cast(int)(value - ahs); 149 else 150 return mixin("value.opCmp(ahs)"); 151 } 152 153 auto opOpAssign(string op, R)(R rhs) 154 { 155 if (ptr == null) 156 throw new Throwable("Null object reference T.T"); 157 158 mixin("value "~op~"= rhs;"); 159 return this; 160 } 161 162 auto opOpAssign(string op, R)(R rhs) shared 163 { 164 if (ptr == null) 165 throw new Throwable("Null object reference T.T"); 166 167 mixin("value "~op~"= rhs;"); 168 return this; 169 } 170 171 auto opBinary(string op, R)(const R rhs) 172 { 173 if (ptr == null) 174 throw new Throwable("Null object reference T.T"); 175 176 return mixin("Nullable!T(cast(T)(value "~op~" rhs))"); 177 } 178 179 auto opBinary(string op, R)(const R rhs) shared 180 { 181 if (ptr == null) 182 throw new Throwable("Null object reference T.T"); 183 184 return mixin("Nullable!T(cast(T)(value "~op~" rhs))"); 185 } 186 187 auto opBinaryRight(string op, L)(const L lhs) 188 { 189 if (ptr == null) 190 throw new Throwable("Null object reference T.T"); 191 192 return mixin("Nullable!T(cast(T)(lhs "~op~" value));"); 193 } 194 195 auto opBinaryRight(string op, L)(const L lhs) shared 196 { 197 if (ptr == null) 198 throw new Throwable("Null object reference T.T"); 199 200 return mixin("Nullable!T(cast(T)(lhs "~op~" value));"); 201 } 202 203 template opDispatch(string member) 204 { 205 template opDispatch(TARGS...) 206 { 207 auto opDispatch(string member, ARGS...)(ARGS args) 208 { 209 if (ptr == null) 210 throw new Throwable("Null object reference T.T"); 211 212 static if (seqContains!(member, Fields!T)) 213 mixin("return value."~member~" = args[0];"); 214 else static if (seqContains!(member, Functions!T)) 215 mixin("return value."~member~"(args);"); 216 } 217 218 auto opDispatch(string member, ARGS...)(ARGS args) shared 219 { 220 if (ptr == null) 221 throw new Throwable("Null object reference T.T"); 222 223 else static if (seqContains!(member, Functions!T) || 224 __traits(compiles, { mixin("return value."~member~"(args);"); }) || 225 !__traits(compiles, { mixin("return value."~member~" = args[0];"); })) 226 mixin("return value."~member~"(args);"); 227 else 228 mixin("return value."~member~" = args[0];"); 229 } 230 } 231 } 232 233 string toString() const 234 { 235 if (ptr == null) 236 return "null"; 237 238 return to!string(value); 239 } 240 241 string toString() const shared 242 { 243 if (ptr == null) 244 return "null"; 245 246 return to!string(value); 247 } 248 249 void nullify() 250 { 251 ptr = null; 252 } 253 254 void unnullify() 255 { 256 ptr = &value; 257 } 258 } 259 260 /// Helper function for creating a nullable. 261 Nullable!T nullable(T)(T val) 262 { 263 return Nullable!T(val); 264 } 265 266 /// Helper function for creating a nullable. 267 Nullable!T nullable(T)(NULL val) 268 { 269 return Nullable!T(null); 270 } 271 272 /// Highly mutable range wrapper for working with any indexable or sliceable type. 273 public struct Range(T) 274 { 275 T value; 276 alias value this; 277 278 public: 279 final: 280 this(T val) 281 { 282 value = val.qdup; 283 } 284 285 auto opAssign(T)(T val) 286 { 287 return value = val.qdup; 288 } 289 290 void popFront() 291 { 292 value = this[1..$]; 293 } 294 295 void popBack() 296 { 297 value = this[0..$-1]; 298 } 299 300 string toString() 301 { 302 return value.to!string; 303 } 304 305 @nogc: 306 A opCast(A)() 307 { 308 return cast(A)value; 309 } 310 311 size_t length() 312 { 313 static if (__traits(compiles, { auto _ = opDollar(); })) 314 return opDollar(); 315 static assert("Range!"~T.stringof~" has no length!"); 316 } 317 318 ref auto opIndex(size_t index) 319 { 320 static if (__traits(compiles, { auto _ = value[0]; })) 321 return value[index]; 322 static assert("Range!"~T.stringof~" has no indexing!"); 323 } 324 325 auto opIndexAssign(A)(A ahs, size_t index) 326 if (A.sizeof <= ElementType!T.sizeof) 327 { 328 static if (__traits(compiles, { auto _ = (value[0] = ahs); })) 329 return value[index] = ahs; 330 else static if (__traits(compiles, { auto _ = value[0]; })) 331 { 332 memcpy(cast(void*)&ahs, cast(void*)&value[index], A.sizeof); 333 return value[index]; 334 } 335 static assert("Range!"~T.stringof~" has no indexing!"); 336 } 337 338 auto opSlice(size_t start, size_t end) 339 { 340 static if (__traits(compiles, { auto _ = value[start..end]; })) 341 return value[start..end]; 342 static assert("Range!"~T.stringof~" has no slicing!"); 343 } 344 345 auto opSliceAssign(A)(A ahs, size_t start, size_t end) 346 if (ElementType!A.sizeof <= ElementType!T.sizeof) 347 { 348 static if (__traits(compiles, { auto _ = (value[start..end] = ahs); })) 349 return value[start..end] = ahs; 350 else static if (__traits(compiles, { auto _ = value[start]; })) 351 { 352 memcpy(cast(void*)ahs.ptr, cast(void*)&value[start], ElementType!A.sizeof * ahs.length); 353 return value[start..end]; 354 } 355 return value; 356 } 357 358 size_t opDollar(size_t DIM : 0)() 359 { 360 static if (__traits(compiles, { auto _ = value.opDollar!DIM; })) 361 return value.opDollar!DIM; 362 else static if (DIM == 0) 363 return opDollar(); 364 else 365 { 366 size_t length; 367 foreach (u; value[DIM]) 368 length++; 369 return length; 370 } 371 static assert("Range!"~T.stringof~" has no length!"); 372 } 373 374 size_t opDollar() 375 { 376 static if (__traits(compiles, { auto _ = value.opDollar(); })) 377 return value.opDollar(); 378 else static if (__traits(compiles, { auto _ = value.length; })) 379 return value.length; 380 else 381 { 382 size_t length; 383 foreach (u; value) 384 length++; 385 return length; 386 } 387 static assert("Range!"~T.stringof~" has no length!"); 388 } 389 390 auto front() 391 { 392 return this[0]; 393 } 394 395 auto back() 396 { 397 return this[$-1]; 398 } 399 400 bool empty() 401 { 402 return length == 0; 403 } 404 }