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 }