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 }