1 module dmocks.dynamic; 2 3 import std.conv; 4 import std.traits; 5 import std.typecons; 6 7 /++ 8 + This is a very very simple class for storing a variable regardless of it's size and type 9 +/ 10 abstract class Dynamic 11 { 12 // toHash, toString and opEquals are also part of this class 13 // but i'm not sure how to express that in code so this comment has to be enough:) 14 15 /// returns stored typeinfo 16 abstract TypeInfo type(); 17 /// converts stored value to given "to" type and returns 1el array of target type vals or null when conversion failed 18 abstract void[] convertTo(TypeInfo to); 19 20 /// returns true if variable held by dynamic is convertible to given type 21 bool canConvertTo(TypeInfo to) 22 { 23 return type==to || convertTo(to) !is null; 24 } 25 } 26 27 /// returns stored value if type T is precisely the type of variable stored, variable stored can be implicitly to that type 28 T get(T)(Dynamic d) 29 { 30 // in addition to init property requirement disallow user defined value types which can have alias this to null-able type 31 static if (!is(T==union) && !is(T==struct) && is(typeof(T.init is null))) 32 { 33 if (d.type == typeid(typeof(null))) 34 return null; 35 } 36 if (d.type == typeid(T)) 37 return ((cast(DynamicT!T)d).data()); 38 void[] convertResult = d.convertTo(typeid(T)); 39 return (cast(T*)convertResult)[0]; 40 } 41 42 /// a helper function for creating Dynamic obhects 43 Dynamic dynamic(T)(auto ref T t) 44 { 45 return new DynamicT!T(t); 46 } 47 48 class DynamicT(T) : Dynamic 49 { 50 private T _data; 51 this(T t) 52 { 53 _data = t; 54 } 55 56 /// 57 override TypeInfo type() 58 { 59 return typeid(T); 60 } 61 62 /// 63 override string toString() 64 { 65 return _data.to!string(); 66 } 67 68 /// two dynamics are equal when they store same type and the values pass opEquals 69 override bool opEquals(Object object) 70 { 71 auto dyn = cast(DynamicT!T)object; 72 if (dyn is null) 73 return false; 74 if (dyn.type != type) 75 return false; 76 77 return _data == dyn._data; 78 } 79 80 /// 81 override size_t toHash() 82 { 83 return typeid(T).getHash(&_data); 84 } 85 86 /// 87 T data() 88 { 89 return _data; 90 } 91 92 /// 93 override void[] convertTo(TypeInfo to) 94 { 95 foreach(target;ImplicitConversionTargets!(T)) 96 { 97 if (typeid(target) == to) 98 { 99 auto ret = new target[1]; 100 ret[0] = cast(target)_data; 101 return ret; 102 } 103 } 104 return null; 105 } 106 } 107 108 version (DMocksTest) { 109 110 class A 111 { 112 } 113 114 class B : A 115 { 116 } 117 118 119 unittest 120 { 121 auto d = dynamic(6); 122 assert(d.toString == "6"); 123 assert(d.type.toString == "int"); 124 auto e = dynamic(6); 125 assert(e == d); 126 assert(e.get!int == 6); 127 } 128 129 unittest 130 { 131 auto d = dynamic(new B); 132 assert(d.get!A !is null); 133 assert(d.get!B !is null); 134 } 135 136 unittest 137 { 138 auto d = dynamic(null); 139 assert(d.get!A is null); 140 } 141 142 struct C 143 { 144 } 145 146 struct D 147 { 148 private C _c; 149 alias _c this; 150 } 151 152 unittest { 153 int[5] a; 154 auto d = dynamic(a); 155 assert(d.get!(int[5]) == [0,0,0,0,0]); 156 } 157 158 /+ ImplicitConversionTargets doesn't include alias thises 159 unittest 160 { 161 auto d = dynamic(D()); 162 d.get!C; 163 d.get!D; 164 } 165 +/ 166 }