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 or null when conversion failed 17 abstract void[] convertTo(TypeInfo to); 18 19 /// returns true if variable held by dynamic is convertible to given type 20 bool canConvertTo(TypeInfo to) 21 { 22 return type==to || convertTo(to) !is null; 23 } 24 } 25 26 /// returns stored value if type T is precisely the type of variable stored, variable stored can be implicitly to that type 27 T get(T)(Dynamic d) 28 { 29 // in addition to init property requirement disallow user defined value types which can have alias this to null-able type 30 static if (!is(T==union) && !is(T==struct) && is(typeof(T.init is null))) 31 { 32 if (d.type == typeid(typeof(null))) 33 return null; 34 } 35 if (d.type == typeid(T)) 36 return ((cast(DynamicT!T)d).data()); 37 void[] convertResult = d.convertTo(typeid(T)); 38 return (cast(T*)convertResult)[0]; 39 } 40 41 /// a helper function for creating Dynamic obhects 42 Dynamic dynamic(T)(auto ref T t) 43 { 44 return new DynamicT!T(t); 45 } 46 47 class DynamicT(T) : Dynamic 48 { 49 private T _data; 50 this(T t) 51 { 52 _data = t; 53 } 54 55 /// 56 override TypeInfo type() 57 { 58 return typeid(T); 59 } 60 61 /// 62 override string toString() 63 { 64 static if (is(typeof(_data) == class) && !hasFunctionAttributes!(typeof(_data).toString, "const")) 65 { 66 return (cast(Unqual!(typeof(_data))) _data).toString(); 67 } 68 else 69 { 70 return _data.to!string; 71 } 72 } 73 74 /// two dynamics are equal when they store same type and the values pass opEquals 75 override bool opEquals(Object object) 76 { 77 auto dyn = cast(DynamicT!T)object; 78 if (dyn is null) 79 return false; 80 if (dyn.type != type) 81 return false; 82 83 return _data == dyn._data; 84 } 85 86 /// 87 override size_t toHash() 88 { 89 return typeid(T).getHash(&_data); 90 } 91 92 /// 93 T data() 94 { 95 return _data; 96 } 97 98 /// 99 override void[] convertTo(TypeInfo to) 100 { 101 foreach(target;ImplicitConversionTargets!(T)) 102 { 103 if (typeid(target) == to) 104 { 105 auto ret = new target[1]; 106 ret[0] = cast(target)_data; 107 return ret; 108 } 109 } 110 return null; 111 } 112 } 113 114 version (unittest) 115 { 116 class A 117 { 118 } 119 120 class B : A 121 { 122 } 123 124 struct C 125 { 126 } 127 128 struct D 129 { 130 private C _c; 131 alias _c this; 132 } 133 } 134 135 unittest 136 { 137 auto d = dynamic(6); 138 assert(d.toString == "6"); 139 assert(d.type.toString == "int"); 140 auto e = dynamic(6); 141 assert(e == d); 142 assert(e.get!int == 6); 143 } 144 145 unittest 146 { 147 auto d = dynamic(new B); 148 assert(d.get!A !is null); 149 assert(d.get!B !is null); 150 } 151 152 unittest 153 { 154 auto d = dynamic(null); 155 assert(d.get!A is null); 156 } 157 158 unittest 159 { 160 int[5] a; 161 auto d = dynamic(a); 162 assert(d.get!(int[5]) == [0,0,0,0,0]); 163 } 164 165 /+ ImplicitConversionTargets doesn't include alias thises 166 unittest 167 { 168 auto d = dynamic(D()); 169 d.get!C; 170 d.get!D; 171 } 172 +/ 173 174 @("supports const object with non-const toString") 175 unittest 176 { 177 static assert(is(typeof(dynamic(new const Object)))); 178 static assert(is(typeof(dynamic(new immutable Object)))); 179 }