1 /******************************************************************************* 2 3 Functions to help with type conversion. 4 5 Copyright: 6 Copyright (c) 2009-2016 dunnhumby Germany GmbH. 7 All rights reserved. 8 9 License: 10 Boost Software License Version 1.0. See LICENSE_BOOST.txt for details. 11 Alternatively, this file may be distributed under the terms of the Tango 12 3-Clause BSD License (see LICENSE_BSD.txt for details). 13 14 *******************************************************************************/ 15 16 module ocean.core.TypeConvert; 17 18 import ocean.meta.traits.Indirections; 19 import ocean.meta.types.Qualifiers; 20 import ocean.meta.types.Function; 21 import ocean.meta.types.Typedef; 22 23 version (unittest) import ocean.core.Test; 24 25 26 /******************************************************************************* 27 28 Trivial wrapper for a cast from any array to an array of immutable elements 29 (e.g. from any string to an immutable string), to make code more readable. 30 Its use is only legal if no one else has a reference to the contents of the 31 `input` array. Cf. `std.exception.assumeUnique` in Phobos. 32 33 Params: 34 input = slice whose contents to cast to immutable; this reference 35 will be nullified to prevent any further access from this 36 mutable handle 37 38 Returns: 39 slice of immutable elements corresponding to the same segment of 40 memory referred to by `input` 41 42 Note: 43 D1 does not allow overloading on rvalue vs lvalue, nor does it have 44 anything similar to D2's `auto ref` feature. At the same time, to 45 match Phobos semantics we need to nullify the slice that gets cast 46 to immutable. Because of this in D1 `assumeUnique` accepts only 47 rvalues: use temporary local variables to assign lvalues if any 48 need to be used with `assumeUnique`. 49 50 D2 programs can use lvalues as well as rvalues. 51 52 Credits: 53 This function is copied from phobos `std.exception.assumeUnique` 54 ((c) Andrei Alexandrescu, Boost license) with minor modifications. 55 56 *******************************************************************************/ 57 58 public immutable(T)[] assumeUnique (T) (T[] input) 59 { 60 return .assumeUnique(input); 61 } 62 63 unittest 64 { 65 auto s = assumeUnique("1234".dup); 66 static assert(is(typeof(s) == istring)); 67 test!("==")(s, "1234"); 68 } 69 70 public immutable(T)[] assumeUnique (T) (ref T[] input) 71 { 72 auto tmp = input; 73 input = null; 74 return cast(immutable(T)[]) tmp; 75 } 76 77 unittest 78 { 79 auto s1 = "aaa".dup; 80 auto s2 = assumeUnique(s1); 81 test!("==")(s2, "aaa"); 82 test!("is")(s1, mstring.init); 83 } 84 85 86 /******************************************************************************* 87 88 Casts an object of one class to another class. Using this function is safer 89 than a plain cast, as it also statically ensures that the variable being 90 cast from is a class or an interface. 91 92 Params: 93 To = type to cast to (must be a class) 94 From = type to cast from (must be a class or interface) 95 value = object to be cast to type To 96 97 Returns: 98 input parameter cast to type To. The returned object may be null if the 99 From cannot be downcast to To 100 101 *******************************************************************************/ 102 103 public To downcast ( To, From ) ( From value ) 104 { 105 static assert(is(To == class)); 106 static assert(is(From == class) || is(From == interface)); 107 108 return cast(To)value; 109 } 110 111 version (unittest) 112 { 113 class A { } 114 class B : A { } 115 interface I { } 116 class C : I { } 117 } 118 119 unittest 120 { 121 // Basic type does not compile 122 static assert(!is(typeof({ int i; downcast!(Object)(i); }))); 123 124 // Pointer to object does not compile 125 static assert(!is(typeof({ Object* o; downcast!(Object)(o); }))); 126 127 // Object compiles 128 static assert(is(typeof({ Object o; downcast!(Object)(o); }))); 129 130 // Interface compiles 131 static assert(is(typeof({ I i; downcast!(Object)(i); }))); 132 133 // Downcast succeeds for derived class 134 { 135 A a = new B; 136 B b = downcast!(B)(a); 137 test!("!is")(cast(void*)b, null); 138 } 139 140 // Downcast succeeds for derived interface 141 { 142 I i = new C; 143 C c = downcast!(C)(i); 144 test!("!is")(cast(void*)c, null); 145 } 146 147 // Downcast fails for non-derived class 148 { 149 A a = new B; 150 C c = downcast!(C)(a); 151 test!("is")(cast(void*)c, null); 152 } 153 154 // Downcast fails for non-derived interface 155 { 156 I i = new C; 157 B b = downcast!(B)(i); 158 test!("is")(cast(void*)b, null); 159 } 160 } 161 162 163 /******************************************************************************* 164 165 Explicit cast function -- both from and to types must be specified by the 166 user and are statically ensured to be correct. This extra security can help 167 prevent refactoring errors. 168 169 Usage: 170 --- 171 int i; 172 float f = castFrom!(int).to!(float)(i); 173 --- 174 175 Params: 176 From = type to cast from 177 178 ******************************************************************************/ 179 180 template castFrom ( From ) 181 { 182 183 /************************************************************************* 184 185 Explicit cast function -- both from and to types must be specified by 186 the user and are statically ensured to be correct. This extra security 187 can help prevent refactoring errors. 188 189 Usage: 190 --- 191 int i; 192 float f = castFrom!(int).to!(float)(i); 193 --- 194 195 Params: 196 From = type to cast from 197 To = type to cast to 198 T = type of value being cast (statically checked to be == From) 199 value = value to be cast to type To 200 201 Returns: 202 input parameter cast to type To 203 204 **************************************************************************/ 205 206 To to ( To, T ) ( T value ) 207 { 208 static assert( 209 is(From == T), 210 "the value to cast is not of specified type '" ~ From.stringof ~ 211 "', it is of type '" ~ T.stringof ~ "'" 212 ); 213 214 static assert( 215 is(typeof(cast(To)value)), 216 "can't cast from '" ~ From.stringof ~ "' to '" ~ To.stringof ~ "'" 217 ); 218 219 return cast(To)value; 220 } 221 } 222 223 unittest 224 { 225 // Mismatched From does not compile 226 static assert(!is(typeof({ int x; castFrom!(float).to!(char)(x); }))); 227 228 // Mismatched but implicitly castable From does not compile 229 static assert(!is(typeof({ double x; castFrom!(float).to!(char)(x); }))); 230 231 // Illegal cast does not compile 232 static assert(!is(typeof({ void* p; castFrom!(void*).to!(int[30])(p); }))); 233 234 // Valid case compiles 235 static assert(is(typeof({ int x; castFrom!(int).to!(float)(x); }))); 236 } 237 238 239 /******************************************************************************* 240 241 Creates a new array from the elements supplied as function arguments, 242 casting each of them to T. 243 244 Params: 245 T = type of element of new array 246 original = original elements of a type that can be cast to T safely 247 248 /******************************************************************************/ 249 250 template arrayOf (T) 251 { 252 T[] arrayOf (U...) (U original) 253 { 254 static assert (U.length > 0); 255 static assert (!hasIndirections!(U[0])); 256 static assert (!hasIndirections!(T)); 257 258 // workaround for dmd1 semantic analysis bug 259 auto unused = original[0]; 260 261 static istring generateCast ( ) 262 { 263 istring result = "[ "; 264 265 foreach (i, _; U) 266 { 267 result ~= "cast(T) original[" ~ i.stringof ~ "]"; 268 if (i + 1 < U.length) 269 result ~= ", "; 270 } 271 272 return result ~ " ]"; 273 } 274 275 return mixin(generateCast()); 276 } 277 } 278 279 version (unittest) 280 { 281 static immutable _arrayOf_global_scope = arrayOf!(byte)(1, 2, 3); 282 } 283 284 /// 285 unittest 286 { 287 auto arr = arrayOf!(hash_t)(1, 2, 3); 288 test!("==")(arr, [ cast(hash_t) 1, 2, 3 ][]); 289 } 290 291 unittest 292 { 293 // ensure it works with Typedef structs in D2 294 mixin (Typedef!(hash_t, "Hash")); 295 auto arr = arrayOf!(Hash)(1, 2, 3); 296 297 // ensure it works in CTFE 298 static immutable manifest = arrayOf!(long)(42, 44, 46); 299 static assert (manifest.length == 3); 300 static assert (manifest[0] == 42L); 301 static assert (manifest[1] == 44L); 302 static assert (manifest[2] == 46L); 303 304 // reject stuff with indirections 305 static assert (!is(typeof(arrayOf!(int*)(1000)))); 306 static assert (!is(typeof(arrayOf!(int)((int[]).init)))); 307 } 308 309 310 /******************************************************************************* 311 312 Generates delegate that stores specified `context` as a delegate context 313 pointer and, when called, forwards it to function `F` as a regular argument. 314 315 Intended to be used as a performance optimization hack to create 316 no-allocation closures that only need to capture one pointer size argument. 317 318 Params: 319 F = function to call when delegate is called, must take exactly one 320 void* argument which is the passed context 321 context = context pointer to forward to F when resulting delegate is 322 called 323 324 Returns: 325 forged delegate that can be passed to any API expecting regular `T 326 delegate()` where T is the return type of F 327 328 *******************************************************************************/ 329 330 ReturnTypeOf!(F) delegate() toContextDg ( alias F ) ( void* context ) 331 { 332 static assert (is(typeof({ F((void*).init); }))); 333 334 // This code makes use of two facts: 335 // 1) The D ABI allows aggregate methods to be converted to delegates, 336 // such that the delegate context pointer becomes the `this` pointer 337 // of the aggregate 338 // 2) The compiler supports explicit modification of the .ptr member of a 339 // delegate, without modifying the existing .functptr. 340 341 static struct Fake 342 { 343 ReturnTypeOf!(F) method ( ) 344 { 345 void* context = cast(void*) &this; 346 347 // do real work via provided F function: 348 return F(context); 349 } 350 } 351 352 auto dg = &Fake.init.method; 353 dg.ptr = context; 354 return dg; 355 } 356 357 /// 358 unittest 359 { 360 static bool done = false; 361 362 static void handler ( void* context ) 363 { 364 test!("==")(cast(size_t) context, 42); 365 done = true; 366 } 367 368 void delegate() dg = toContextDg!(handler)(cast(void*) 42); 369 test!("==")(cast(size_t) dg.ptr, 42); 370 dg(); 371 test(done); 372 } 373 374 /******************************************************************************* 375 376 Helper function to wrap any callable type in a delegate. Most useful when 377 you need to pass function pointer as a delegate argument. 378 379 This function allocates a closure class for a delegate. 380 381 NB! toDg does not preserve any argument attributes of Func such as ref or 382 lazy. 383 384 Params: 385 f = function or function pointer or delegate 386 387 Returns: 388 delegate that internally calls f and does nothing else 389 390 *******************************************************************************/ 391 392 ReturnTypeOf!(Func) delegate (ParametersOf!(Func)) toDg ( Func ) ( Func f ) 393 { 394 static assert ( 395 is(Func == ReturnTypeOf!(Func) function (ParametersOf!(Func))), 396 "toDg does not preserve argument attributes!" 397 ); 398 399 alias ParametersOf!(Func) ParameterTypes; 400 401 class Closure 402 { 403 private Func func; 404 405 this (Func func) 406 { 407 this.func = func; 408 } 409 410 ReturnTypeOf!(Func) call (ParameterTypes args) 411 { 412 return this.func(args); 413 } 414 } 415 416 auto closure = new Closure(f); 417 418 return &closure.call; 419 } 420 421 version (unittest) 422 { 423 int testToDgFoo() { return 42; } 424 425 void testToDgBar(int a, int b) 426 { 427 assert (a == 3); 428 assert (b == 4); 429 } 430 431 int testToDgBad(ref int x) { return x; } 432 } 433 434 unittest 435 { 436 static assert (is(typeof(toDg(&testToDgFoo)) == int delegate())); 437 test (toDg(&testToDgFoo)() == 42); 438 439 toDg(&testToDgBar)(3, 4); 440 441 static assert(!is(typeof(toDg(&testToDgBad)))); 442 }