1 /**
2 Type casting functions.
3 
4 Functions from this module are intended to be used instead of
5 build-in $(HTTP dlang.org/expression.html#CastExpression, $(D cast))
6 as they provide safer and richer interface.
7 
8 Copyright: Denis Shelomovskij 2013
9 
10 License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
11 
12 Authors: Denis Shelomovskij
13 */
14 module unstd.casts;
15 
16 
17 import std..string;
18 
19 
20 @safe pure nothrow:
21 
22 /**
23 Functions for $(D class)/$(D interface) dynamic casting.
24 
25 These functions don't change type qualifier.
26 
27 $(D toRawPtr)/$(D fromRawPtr) should be used to safely convert class instances
28 to raw $(D void*) instead of $(MREF viewAs) because $(D interface) variables
29 don't point to a beginning of a class instance.
30 
31 Preconditions:
32 Passed object (pointer for $(D fromRawPtr)) is not $(D null).
33 
34 Note:
35 $(HTTP dlang.org/class.html#AliasThis, $(D alias this)) isn't considered while casting
36 in contrary to build-in $(HTTP dlang.org/expression.html#CastExpression, $(D cast)).
37 */
38 enum typesDynamicCastable(To, From) =
39 	(is(From == class) || is(From == interface)) && (is(To == class) || is(To == interface))
40 		&& (is(From == interface) || is(To == interface) || is(To : From) || is(From : To));
41 
42 /// ditto
43 @property inout(To) tryDynamicCast(To, From)(inout(From) o) @trusted @nogc
44 if(typesDynamicCastable!(To, From))
45 {
46 	return o._dynamicCastImpl!To;
47 }
48 
49 /// ditto
50 @property bool dynamicCastable(To, From)(inout(From) o) @trusted @nogc
51 if(typesDynamicCastable!(To, From))
52 {
53 	return !!o._dynamicCastImpl!To;
54 }
55 
56 /// ditto
57 @property inout(To) dynamicCast(To, From)(inout(From) o) @trusted @nogc
58 if(typesDynamicCastable!(To, From))
59 in { assert(o.dynamicCastable!To); }
60 body
61 {
62 	return o._dynamicCastImpl!(To, true);
63 }
64 
65 
66 /// ditto
67 enum typesDowncastable(To, From) =
68 	(is(From == class) || is(From == interface)) && (is(To : From) || is(From == Object));
69 
70 /// ditto
71 @property inout(To) tryDowncast(To, From)(inout(From) o) @trusted @nogc
72 if(typesDowncastable!(To, From))
73 {
74 	return o._dynamicCastImpl!To;
75 }
76 
77 /// ditto
78 @property bool downcastable(To, From)(inout(From) o) @trusted @nogc
79 if(typesDowncastable!(To, From))
80 {
81 	return !!o._dynamicCastImpl!To;
82 }
83 
84 /// ditto
85 @property inout(To) downcast(To, From)(inout(From) o) @trusted @nogc
86 if(typesDowncastable!(To, From))
87 in { assert(o.downcastable!To); }
88 body
89 {
90 	return o._dynamicCastImpl!(To, true);
91 }
92 
93 
94 /// ditto
95 enum typesUpcastable(To, From) =
96 	(is(To == class) || is(To == interface)) && (is(From : To) || is(To == Object));
97 
98 /// ditto
99 @property inout(To) upcast(To, From)(inout(From) o) @trusted @nogc
100 if(typesUpcastable!(To, From))
101 {
102 	return o._dynamicCastImpl!(To, true);
103 }
104 
105 
106 /// ditto
107 @property inout(void)* toRawPtr(From)(inout(From) o) @trusted @nogc
108 if(is(From == class) || is(From == interface))
109 {
110 	return o.upcast!Object.viewAs!(void*);
111 }
112 
113 /// ditto
114 @property inout(To) fromRawPtr(To)(inout(void)* p) @system @nogc
115 if(is(To == class) || is(To == interface))
116 {
117 	return p.viewAs!Object.downcast!To;
118 }
119 
120 
121 ///
122 unittest
123 {
124 	class A { }
125 	class B { }
126 
127 	A a = new A;
128 	Object o = a;
129 	assert(o.downcast!A is a);
130 	assert(!o.downcastable!B);
131 	static assert(!__traits(compiles, a.dynamicCast!B)); // cast impossible
132 }
133 
134 ///
135 @trusted unittest
136 {
137 	interface I { }
138 	class A : I { }
139 
140 	A a = new A;
141 	I i = a;
142 	void* p = a.toRawPtr;
143 	// `i` doesn't point to a beginning of a class instance:
144 	assert(i.viewAs!(void*) != p);
145 	assert(i.toRawPtr == p);
146 	assert(p.fromRawPtr!I is a); // `fromRawPtr` is `@system`
147 }
148 
149 unittest
150 {
151 	class A { }
152 	class B: A { }
153 
154 	B b = new B;
155 	A a = b;
156 	assert(a.downcast!B is b);
157 	assert(b.upcast!A is a);
158 	static assert(!__traits(compiles, b.downcast!A));
159 	static assert(!__traits(compiles, a.upcast!B));
160 	const ca = a;
161 	static assert(is(typeof(ca.downcast!B) == const B));
162 	const cb = b;
163 	static assert(is(typeof(cb.upcast!A) == const A));
164 
165 
166 	class X { }
167 	X x;
168 	static assert(!__traits(compiles, x.downcast!A));
169 	static assert(!__traits(compiles, x.dynamicCast!A));
170 }
171 
172 unittest
173 {
174 	interface I { }
175 	class C: I { }
176 	C c = new C;
177 	I i = c;
178 	assert(c.upcast!I is i);
179 	Object o = c;
180 	assert(o.downcast!I is c);
181 	assert(i.upcast!Object is c);
182 
183 	assert(i.dynamicCast!C is c);
184 	assert(o.dynamicCast!C is c);
185 	assert(o.dynamicCast!I is i);
186 
187 	void* p = o.toRawPtr;
188 	assert(c.toRawPtr == p);
189 	assert(i.toRawPtr == p);
190 	() @trusted
191 	{
192 		assert(p.fromRawPtr!C is c);
193 		assert(p.fromRawPtr!I is i);
194 	} ();
195 }
196 
197 unittest
198 {
199 	interface I1 { }
200 	interface I2 { }
201 	class C: I1, I2 { int n; alias n this; }
202 	class D: C {}
203 	D d = new D;
204 	C c = d;
205 	I1 i1 = c;
206 	I2 i2 = c;
207 	Object o = c;
208 	assert(o.downcast!I1 is c);
209 	assert(i2.dynamicCast!I1 is c);
210 
211 	assert(c.upcast!I1() is c);
212 	assert(c.dynamicCast!I2 is c);
213 
214 	assert(c.downcast!D() is d);
215 	assert(!new C().downcastable!D());
216 }
217 
218 
219 private @property inout(To) _dynamicCastImpl(To, bool castExists = false, From)(inout(From) o) @system @nogc
220 if(typesDynamicCastable!(To, From))
221 //in { assert(o, format("Attempt to perform dynamic cast of `null` from `%s` to `%s`.", From.stringof, To.stringof)); }
222 body
223 {
224 	static if(is(From == class) && is(To == class) && castExists)
225 	{
226 		return o.viewAs!To;
227 	}
228 	else
229 	{
230 		static if(is(From == class)) alias rtCast = _d_dynamic_cast;
231 		else alias rtCast = _d_interface_cast;
232 		return rtCast(o.viewAs!(void*), To.classinfo).viewAs!To;
233 	}
234 }
235 
236 extern(C) @system @nogc
237 {
238 	inout(void)* _d_dynamic_cast(inout(void)* p, in ClassInfo c);
239 	inout(void)* _d_interface_cast(inout(void)* p, in ClassInfo c);
240 }
241 
242 
243 /**
244 Function allowing "blind" casting to any type of the same size.
245 
246 No operations on actial value performed.
247 It is just treated as having different type.
248 
249 These functions don't change type qualifier.
250 
251 Note:
252 $(HTTP dlang.org/class.html#AliasThis, $(D alias this)) isn't considered while casting
253 in contrary to build-in $(HTTP dlang.org/expression.html#CastExpression, $(D cast)).
254 
255 Warning:
256 Resulting value may depend on target architecture.
257 */
258 @property inout(To) viewAs(To, From)(inout(From) val) @system @nogc
259 {
260 	return val.viewAs!To;
261 }
262 
263 /// ditto
264 @property ref inout(To) viewAs(To, From)(ref inout(From) val) @system @nogc
265 {
266 	static assert(To.sizeof == From.sizeof,
267 		format("Type size mismatch in `viewAs`: %s.sizeof(%s) != %s.sizeof(%s)",
268 		To.stringof, To.sizeof, From.stringof, From.sizeof));
269 	return *cast(inout(To)*) &val;
270 }
271 
272 ///
273 @trusted @nogc unittest
274 {
275 	int i = 0;
276 	i.viewAs!(ubyte[4]) = 3;
277 	assert(i == 0x03030303);
278 }
279 
280 @trusted @nogc unittest
281 {
282 	assert(1.viewAs!uint == 1);
283 	static assert(!__traits(compiles, 1.viewAs!ulong));
284 	assert(!Object.init.viewAs!size_t);
285 
286 	int i = 0;
287 	++i.viewAs!uint;
288 	assert(i == 1);
289 	i.viewAs!(ushort[2]) = 1;
290 	assert(i == 0x00010001);
291 
292 	const int ci = i;
293 	static assert(!__traits(compiles, ++ci.viewAs!uint));
294 	static assert(is(typeof(ci.viewAs!(ushort[2])) == const(ushort[2])));
295 }