1 module dmocks.dynamic; 2 3 import std.conv; 4 import std.traits; 5 6 /++ 7 + This is a very very simple class for storing a variable regardless of it's size and type 8 +/ 9 abstract class Dynamic 10 { 11 // toHash, toString and opEquals are also part of this class 12 // but i'm not sure how to express that in code so this comment has to be enough:) 13 14 /// returns stored typeinfo 15 abstract TypeInfo type(); 16 /// converts stored value to given "to" type and returns 1el array of target type vals. conversion must be defined. 17 abstract void[] convertTo(TypeInfo to); 18 /// checks if stored value can be converted to given "to" type. 19 abstract bool canConvertTo(TypeInfo to); 20 } 21 22 /// returns stored value if type T is precisely the type of variable stored, variable stored can be implicitly to that type 23 T get(T)(Dynamic d) 24 { 25 import std.exception : enforce; 26 import std.format : format; 27 28 // in addition to init property requirement disallow user defined value types which can have alias this to null-able type 29 static if (!is(T==union) && !is(T==struct) && is(typeof(T.init is null))) 30 { 31 if (d.type == typeid(typeof(null))) 32 return null; 33 } 34 if (d.type == typeid(T)) 35 return ((cast(DynamicT!T)d).data()); 36 37 enforce(d.canConvertTo(typeid(T)), format!"Cannot convert stored value of type '%s' to '%s'!"(d.type, T.stringof)); 38 39 void[] convertResult = d.convertTo(typeid(T)); 40 41 return (cast(T*)convertResult.ptr)[0]; 42 } 43 44 /// a helper function for creating Dynamic obhects 45 Dynamic dynamic(T)(auto ref T t) 46 { 47 return new DynamicT!T(t); 48 } 49 50 class DynamicT(T) : Dynamic 51 { 52 private T _data; 53 this(T t) 54 { 55 _data = t; 56 } 57 58 /// 59 override TypeInfo type() 60 { 61 return typeid(T); 62 } 63 64 /// 65 override string toString() 66 { 67 static if (is(typeof(_data) == class) && !hasFunctionAttributes!(typeof(_data).toString, "const")) 68 { 69 return (cast(Unqual!(typeof(_data))) _data).toString(); 70 } 71 else 72 { 73 return _data.to!string; 74 } 75 } 76 77 /// two dynamics are equal when they store same type and the values pass opEquals 78 override bool opEquals(Object object) 79 { 80 auto dyn = cast(DynamicT!T)object; 81 if (dyn is null) 82 return false; 83 if (dyn.type != type) 84 return false; 85 86 return _data == dyn._data; 87 } 88 89 /// 90 override size_t toHash() 91 { 92 return typeid(T).getHash(&_data); 93 } 94 95 /// 96 T data() 97 { 98 return _data; 99 } 100 101 /// 102 override bool canConvertTo(TypeInfo to) 103 { 104 import std.algorithm : any; 105 106 static if (is(T == typeof(null))) 107 { 108 if (cast(TypeInfo_Array) to || cast(TypeInfo_Pointer) to) 109 { 110 return true; 111 } 112 } 113 114 enum getTypeId(T) = typeid(T); 115 alias ConversionTargets = ImplicitConversionTargets!T; 116 117 static if (ConversionTargets.length) 118 { 119 return type == to || [staticMap!(getTypeId, ConversionTargets)].any!(t => t == to); 120 } 121 else 122 { 123 return type == to; 124 } 125 } 126 127 /// 128 override void[] convertTo(TypeInfo to) 129 { 130 static if (is(T == typeof(null))) 131 { 132 if (cast(TypeInfo_Array) to) 133 { 134 auto ret = new void[][1]; 135 ret[0] = null; 136 return ret; 137 } 138 if (cast(TypeInfo_Pointer) to) 139 { 140 auto ret = new void*[1]; 141 ret[0] = null; 142 return ret; 143 } 144 } 145 146 foreach(Target; ImplicitConversionTargets!(T)) 147 { 148 if (typeid(Target) == to) 149 { 150 auto ret = new Target[1]; 151 ret[0] = cast(Target) _data; 152 return ret; 153 } 154 } 155 156 assert(false); 157 } 158 } 159 160 version (unittest) 161 { 162 class A 163 { 164 } 165 166 class B : A 167 { 168 } 169 170 struct C 171 { 172 } 173 174 struct D 175 { 176 private C _c; 177 alias _c this; 178 } 179 } 180 181 unittest 182 { 183 auto d = dynamic(6); 184 assert(d.toString == "6"); 185 assert(d.type.toString == "int"); 186 auto e = dynamic(6); 187 assert(e == d); 188 assert(e.get!int == 6); 189 } 190 191 unittest 192 { 193 auto d = dynamic(new B); 194 assert(d.get!A !is null); 195 assert(d.get!B !is null); 196 } 197 198 unittest 199 { 200 auto d = dynamic(null); 201 assert(d.get!A is null); 202 } 203 204 unittest 205 { 206 int[5] a; 207 auto d = dynamic(a); 208 assert(d.get!(int[5]) == [0,0,0,0,0]); 209 } 210 211 unittest 212 { 213 import std.exception : assertThrown; 214 215 float f; 216 auto d = dynamic(f); 217 218 assertThrown!Exception(d.get!int); 219 } 220 221 /+ ImplicitConversionTargets doesn't include alias thises 222 unittest 223 { 224 auto d = dynamic(D()); 225 d.get!C; 226 d.get!D; 227 } 228 +/ 229 230 @("supports const object with non-const toString") 231 unittest 232 { 233 static assert(is(typeof(dynamic(new const Object)))); 234 static assert(is(typeof(dynamic(new immutable Object)))); 235 }