1 /** 2 The most important data type in Godot. 3 4 Copyright: 5 Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. 6 Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) 7 Copyright (c) 2017-2018 Godot-D contributors 8 9 License: $(LINK2 https://opensource.org/licenses/MIT, MIT License) 10 11 12 */ 13 module godot.core.variant; 14 15 import godot.c; 16 import godot.core; 17 import godot.object; 18 import godot.d.traits; 19 import godot.d.reference; 20 import godot.script; 21 import godot.d.type; 22 23 import std.meta, std.traits; 24 import std.conv : text; 25 import std.range; 26 27 // for tests 28 import godot.node; 29 import godot.resource; 30 31 // ABI type should probably have its own `version`... 32 version(X86_64) 33 { 34 version(DigitalMars) 35 { 36 version(linux) version = GodotSystemV; 37 version(OSX) version = GodotSystemV; 38 version(Posix) version = GodotSystemV; 39 } 40 } 41 42 /// User-defined Variant conversions. 43 /// For structs and classes, constructors and member functions can also be used. 44 unittest 45 { 46 struct A {} 47 static assert(!Variant.compatible!A); 48 49 struct B {} 50 B to(T : B)(Variant v) { return B(); } 51 int to(T : Variant)(B b) { return 1; } 52 static assert(Variant.compatible!B); 53 54 struct C 55 { 56 this(Variant v) {} 57 Variant to(T : Variant)() { return Variant(1); } 58 } 59 static assert(Variant.compatible!C); 60 61 B b; 62 C c; 63 64 Variant vb = b; 65 Variant vc = c; 66 67 b = vb.as!B; 68 c = vc.as!C; 69 } 70 71 /** 72 Godot's tagged union type. 73 74 Primitives, Godot core types, and `GodotObject`-derived classes can be stored in 75 a Variant. Other user-defined D types can be made compatible with Variant by 76 defining `to!CustomType(Variant)` and `to!Variant(CustomType)` functions. 77 78 Properties and method arguments/returns are passed between Godot and D through 79 Variant, so these must use Variant-compatible types. 80 */ 81 struct Variant 82 { 83 package(godot) godot_variant _godot_variant; 84 85 /// 86 enum Type 87 { 88 nil, 89 90 // atomic types 91 bool_, 92 int_, 93 real_, 94 string, 95 96 // math types 97 98 vector2,// 5 99 rect2, 100 vector3, 101 transform2d, 102 plane, 103 quat,// 10 104 aabb, 105 basis, 106 transform, 107 108 // misc types 109 color, 110 node_path,// 15 111 rid, 112 object, 113 dictionary, 114 array, 115 116 // arrays 117 pool_byte_array,// 20 118 pool_int_array, 119 pool_real_array, 120 pool_string_array, 121 pool_vector2_array, 122 pool_vector3_array,// 25 123 pool_color_array, 124 } 125 126 /// GDNative type that gets passed to the C functions 127 alias InternalType = AliasSeq! 128 ( 129 typeof(null), 130 131 godot_bool, 132 long, 133 double, 134 godot_string, 135 136 godot_vector2, 137 godot_rect2, 138 godot_vector3, 139 godot_transform2d, 140 godot_plane, 141 godot_quat, 142 godot_aabb, 143 godot_basis, 144 godot_transform, 145 146 godot_color, 147 godot_node_path, 148 godot_rid, 149 godot_object, 150 godot_dictionary, 151 godot_array, 152 153 godot_pool_byte_array, 154 godot_pool_int_array, 155 godot_pool_real_array, 156 godot_pool_string_array, 157 godot_pool_vector2_array, 158 godot_pool_vector3_array, 159 godot_pool_color_array, 160 ); 161 162 /// D type that this Variant implementation uses 163 alias DType = AliasSeq! 164 ( 165 typeof(null), 166 167 bool, 168 long, 169 double, 170 String, 171 172 Vector2,// 5 173 Rect2, 174 Vector3, 175 Transform2D, 176 Plane, 177 Quat,// 10 178 AABB, 179 Basis, 180 Transform, 181 182 // misc types 183 Color, 184 NodePath,// 15 185 RID, 186 GodotObject, 187 Dictionary, 188 Array, 189 190 // arrays 191 PoolByteArray,// 20 192 PoolIntArray, 193 PoolRealArray, 194 PoolStringArray, 195 PoolVector2Array, 196 PoolVector3Array,// 25 197 PoolColorArray, 198 ); 199 200 /// 201 enum Operator 202 { 203 //comparation 204 equal, 205 notEqual, 206 less, 207 lessEqual, 208 greater, 209 greaterEqual, 210 211 //mathematic 212 add, 213 substract, 214 multiply, 215 divide, 216 negate, 217 positive, 218 modulus, 219 stringConcat, 220 221 //bitwise 222 shiftLeft, 223 shiftRight, 224 bitAnd, 225 bitOr, 226 bitXor, 227 bitNegate, 228 229 //logic 230 and, 231 or, 232 xor, 233 not, 234 235 //containment 236 in_ 237 } 238 239 private enum bool implicit(Src, Dest) = is(Src : Dest) || isImplicitlyConvertible!(Src, Dest); 240 241 private static GodotObject objectToGodot(T)(T o) 242 { 243 return o.getGodotObject; 244 } 245 246 // Conversions for non-core types provided by Variant. 247 // Lower priority than user-defined `to` functions, to allow overriding 248 // default blanket implementations. 249 private alias internalAs(T) = (Variant v) => v.as!T; 250 private enum bool hasInternalAs(T) = __traits(compiles, internalAs!T); 251 private alias internalFrom(T) = (T t) => Variant.from(t); 252 private enum bool hasInternalFrom(T) = __traits(compiles, internalFrom!T); 253 254 /// 255 T as(T)() const if(isStaticArray!T && compatibleFromGodot!(ElementType!T)) 256 { 257 return as!Array.as!T; 258 } 259 260 /// 261 T as(T)() const if((isGodotClass!T && !is(T == GodotObject)) || is(T : Ref!U, U)) 262 { 263 GodotObject o = cast()(as!GodotObject); 264 return o.as!T; 265 } 266 267 /// 268 static Array from(T)(T t) if((isForwardRange!T || isStaticArray!T) && compatibleToGodot!(ElementType!T)) 269 { 270 return Array.from(t); 271 } 272 273 /// 274 GodotType as(T : GodotType)() const 275 { 276 if(type == Type.object) 277 { 278 Ref!Script s = as!Script; 279 if(s) return GodotType(s); 280 else return GodotType.init; 281 } 282 else if(type == Type..string) return GodotType(BuiltInClass(as!String)); 283 else if(type == Type.int_) return GodotType(cast(Variant.Type)(as!int)); 284 else return GodotType.init; 285 } 286 287 /// 288 static Variant from(T : GodotType)(T t) 289 { 290 import sumtype : match; 291 Variant ret; 292 t.match!( 293 (Variant.Type t) { ret = cast(int)t; }, 294 (BuiltInClass c) { ret = c.name; }, 295 (Ref!Script s) { ret = s; } 296 ); 297 return ret; 298 } 299 300 static assert(hasInternalAs!Node, internalAs!Node); 301 static assert(hasInternalAs!(Ref!Resource), internalAs!(Ref!Resource)); 302 static assert(!hasInternalAs!Object); // `directlyCompatible` types not handled by internalAs 303 static assert(hasInternalAs!(int[4]), internalAs!(int[4])); 304 static assert(hasInternalFrom!(int[4]), internalFrom!(int[4])); 305 static assert(!hasInternalAs!(int[])); 306 static assert(hasInternalFrom!(int[]), internalFrom!(int[])); 307 static assert(hasInternalAs!GodotType, internalAs!GodotType); 308 static assert(hasInternalFrom!GodotType, internalFrom!GodotType); 309 static assert(compatible!GodotType); 310 311 private template getToVariantFunction(T) 312 { 313 mixin("import " ~ moduleName!T ~ ";"); 314 alias getToVariantFunction = (T t){ Variant v = t.to!Variant; return v; }; 315 } 316 enum bool hasToVariantFunction(T) = __traits(compiles, getToVariantFunction!T); 317 318 private template getVariantConstructor(T) 319 { 320 alias getVariantConstructor = (Variant v) => T(v); 321 } 322 enum bool hasVariantConstructor(T) = __traits(compiles, getVariantConstructor!T); 323 324 template getFromVariantFunction(T) 325 { 326 mixin("import " ~ moduleName!T ~ ";"); 327 alias getFromVariantFunction = (Variant v){ T ret = v.to!T; return ret; }; 328 } 329 enum bool hasFromVariantFunction(T) = __traits(compiles, getFromVariantFunction!T); 330 331 /// function to convert T to an equivalent Godot type 332 template conversionToGodot(T) 333 { 334 static if(isGodotClass!T) alias conversionToGodot = objectToGodot!T; 335 else static if(is(T : GodotStringLiteral!s, string s)) alias conversionToGodot = (T t) => t.str(); 336 else static if(is(T : Ref!U, U)) alias conversionToGodot = objectToGodot!U; 337 else static if(isIntegral!T) alias conversionToGodot = (T t) => cast(long)t; 338 else static if(isFloatingPoint!T) alias conversionToGodot = (T t) => cast(double)t; 339 else static if(implicit!(T, const(char)[]) || implicit!(T, const(char)*)) 340 alias conversionToGodot = (T t) => String(t); 341 else static if(hasToVariantFunction!T) 342 { 343 alias conversionToGodot = getToVariantFunction!T; 344 } 345 else static if(hasInternalFrom!T) alias conversionToGodot = internalFrom!T; 346 else alias conversionToGodot = void; // none 347 } 348 enum bool convertsToGodot(T) = isCallable!(conversionToGodot!T); 349 alias conversionToGodotType(T) = Unqual!(ReturnType!(conversionToGodot!T)); 350 351 /// function to convert a Godot-compatible type to T 352 template conversionFromGodot(T) 353 { 354 static if(isIntegral!T) alias conversionFromGodot = (long v) => cast(T)v; 355 else static if(isFloatingPoint!T) alias conversionFromGodot = (double v) => cast(T)v; 356 else static if(hasVariantConstructor!T) 357 { 358 alias conversionFromGodot = getVariantConstructor!T; 359 } 360 else static if(hasFromVariantFunction!T) 361 { 362 alias conversionFromGodot = getFromVariantFunction!T; 363 } 364 else alias conversionFromGodot = void; 365 } 366 enum bool convertsFromGodot(T) = isCallable!(conversionFromGodot!T); 367 alias conversionFromGodotType(T) = Unqual!(Parameters!(conversionFromGodot!T)[0]); 368 369 enum bool directlyCompatible(T) = staticIndexOf!(Unqual!T, DType) != -1; 370 template compatibleToGodot(T) 371 { 372 static if(directlyCompatible!T) enum bool compatibleToGodot = true; 373 else enum bool compatibleToGodot = convertsToGodot!T; 374 } 375 template compatibleFromGodot(T) 376 { 377 static if(directlyCompatible!T) enum bool compatibleFromGodot = true; 378 else static if(hasInternalAs!T) enum bool compatibleFromGodot = true; 379 else enum bool compatibleFromGodot = convertsFromGodot!T; 380 } 381 enum bool compatible(R) = compatibleToGodot!(R) && compatibleFromGodot!(R); 382 383 384 /// All target Variant.Types that T could implicitly convert to, as indices 385 private template implicitTargetIndices(T) 386 { 387 private enum bool _implicit(size_t di) = implicit!(T, DType[di]); 388 alias implicitTargetIndices = Filter!(_implicit, aliasSeqOf!(iota(DType.length))); 389 } 390 391 /++ 392 Get the Variant.Type of a compatible D type. Incompatible types return nil. 393 +/ 394 public template variantTypeOf(T) 395 { 396 import std.traits, godot; 397 398 static if(directlyCompatible!T) 399 { 400 enum Type variantTypeOf = EnumMembers!Type[staticIndexOf!(Unqual!T, DType)]; 401 } 402 else static if(convertsToGodot!T) 403 { 404 static if(is(conversionToGodotType!T : Variant)) enum Type variantTypeOf = Type.nil; 405 else enum Type variantTypeOf = EnumMembers!Type[staticIndexOf!( 406 conversionToGodotType!T, DType)]; 407 } 408 else enum Type variantTypeOf = Type.nil; // so the template always returns a Type 409 } 410 411 /// 412 R as(R)() const if(!is(R == Variant) && !is(R==typeof(null)) && (convertsFromGodot!R || directlyCompatible!R)) 413 { 414 static if(directlyCompatible!R) enum VarType = variantTypeOf!R; 415 else static if(is(conversionFromGodotType!R : Variant)) enum VarType = Type.nil; 416 else enum VarType = EnumMembers!Type[staticIndexOf!(conversionFromGodotType!R, DType)]; 417 418 // HACK workaround for DMD issue #5570 419 version(GodotSystemV) enum sV = true; 420 else enum sV = false; 421 static if(VarType == Type.vector3 && sV) 422 { 423 godot_vector3 ret = void; 424 void* _func = cast(void*)_godot_api.godot_variant_as_vector3; 425 void* _this = cast(void*)&this; 426 427 asm @nogc nothrow 428 { 429 mov RDI, _this; 430 call _func; 431 432 mov ret[0], RAX; 433 mov ret[8], EDX; 434 } 435 return *cast(Vector3*)&ret; 436 } 437 else static if(VarType == Type.nil) 438 { 439 return conversionFromGodot!R(this); 440 } 441 else 442 { 443 DType[VarType] ret = void; 444 *cast(InternalType[VarType]*)&ret = mixin("_godot_api.godot_variant_as_"~FunctionAs!VarType~"(&_godot_variant)"); 445 446 static if(directlyCompatible!R) return ret; 447 else 448 { 449 return conversionFromGodot!R(ret); 450 } 451 } 452 } 453 454 this(R)(auto ref R input) if(!is(R : Variant) && !is(R : typeof(null))) 455 { 456 static assert(compatibleToGodot!R, R.stringof~" isn't compatible with Variant."); 457 enum VarType = variantTypeOf!R; 458 459 static if(VarType == Type.nil) 460 { 461 this = conversionToGodot!R(input); 462 } 463 else 464 { 465 mixin("auto Fn = _godot_api.godot_variant_new_"~FunctionNew!VarType~";"); 466 alias PassType = Parameters!Fn[1]; // second param is the value 467 468 alias IT = InternalType[VarType]; 469 470 // handle explicit conversions 471 static if(directlyCompatible!R) alias inputConv = input; 472 else auto inputConv = conversionToGodot!R(input); 473 474 static if(is(IT == Unqual!PassType)) Fn(&_godot_variant, cast(IT)inputConv); // value 475 else Fn(&_godot_variant, cast(IT*)&inputConv); // pointer 476 } 477 } 478 479 pragma(inline, true) 480 void opAssign(T)(in auto ref T input) if(!is(T : Variant) && !is(T : typeof(null))) 481 { 482 import std.conv : emplace; 483 484 _godot_api.godot_variant_destroy(&_godot_variant); 485 emplace!(Variant)(&this, input); 486 } 487 488 static assert(allSatisfy!(compatible, DType)); 489 static assert(!compatible!Object); // D Object 490 491 static assert(directlyCompatible!GodotObject); 492 static assert(directlyCompatible!(const(GodotObject))); 493 static assert(!directlyCompatible!Node); 494 static assert(compatibleFromGodot!Node); 495 static assert(compatibleToGodot!Node); 496 static assert(compatibleFromGodot!(const(Node))); 497 static assert(compatibleToGodot!(const(Node))); 498 static assert(!directlyCompatible!(Ref!Resource)); 499 static assert(compatibleFromGodot!(Ref!Resource)); 500 static assert(compatibleToGodot!(Ref!Resource)); 501 static assert(compatibleFromGodot!(const(Ref!Resource))); 502 static assert(compatibleToGodot!(const(Ref!Resource))); 503 504 private template FunctionAs(Type type) 505 { 506 private enum string name_ = text(type); 507 private enum string FunctionAs = (name_[$-1]=='_')?(name_[0..$-1]):name_; 508 } 509 private template FunctionNew(Type type) 510 { 511 private enum string name_ = text(type); 512 private enum string FunctionNew = (name_[$-1]=='_')?(name_[0..$-1]):name_; 513 } 514 515 @nogc nothrow: 516 this(this) 517 { 518 godot_variant other = _godot_variant; // source Variant still owns this 519 _godot_api.godot_variant_new_copy(&_godot_variant, &other); 520 } 521 522 static Variant nil() 523 { 524 Variant v = void; 525 _godot_api.godot_variant_new_nil(&v._godot_variant); 526 return v; 527 } 528 529 this(in ref Variant other) 530 { 531 _godot_api.godot_variant_new_copy(&_godot_variant, &other._godot_variant); 532 } 533 534 this(T : typeof(null))(in T nil) 535 { 536 _godot_api.godot_variant_new_nil(&_godot_variant); 537 } 538 539 ~this() 540 { 541 _godot_api.godot_variant_destroy(&_godot_variant); 542 } 543 544 Type type() const 545 { 546 return cast(Type)_godot_api.godot_variant_get_type(&_godot_variant); 547 } 548 549 inout(T) as(T : Variant)() inout { return this; } 550 551 pragma(inline, true) 552 void opAssign(T : typeof(null))(in T nil) 553 { 554 _godot_api.godot_variant_destroy(&_godot_variant); 555 _godot_api.godot_variant_new_nil(&_godot_variant); 556 } 557 558 pragma(inline, true) 559 void opAssign(T : Variant)(in T other) 560 { 561 _godot_api.godot_variant_destroy(&_godot_variant); 562 _godot_api.godot_variant_new_copy(&_godot_variant, &other._godot_variant); 563 } 564 565 bool opEquals(in ref Variant other) const 566 { 567 return cast(bool)_godot_api.godot_variant_operator_equal(&_godot_variant, &other._godot_variant); 568 } 569 570 int opCmp(in ref Variant other) const 571 { 572 if(_godot_api.godot_variant_operator_equal(&_godot_variant, &other._godot_variant)) 573 return 0; 574 return _godot_api.godot_variant_operator_less(&_godot_variant, &other._godot_variant)? 575 -1 : 1; 576 } 577 578 bool booleanize() const 579 { 580 return cast(bool)_godot_api.godot_variant_booleanize(&_godot_variant); 581 } 582 583 auto toString() const 584 { 585 String str = as!String; 586 return str.data; 587 } 588 589 /// Is this Variant of the specified `type` or of a subclass of `type`? 590 bool isType(GodotType type) const 591 { 592 import sumtype : match; 593 return type.match!( 594 (Ref!Script script) { 595 GodotObject o = this.as!GodotObject; 596 if(o == null) return false; 597 return script.instanceHas(o); 598 }, 599 (BuiltInClass object) { 600 GodotObject o = this.as!GodotObject; 601 if(o == null) return false; 602 return o.isClass(object.name); 603 }, 604 (Type vt) => this.type == vt 605 ); 606 } 607 608 /++ 609 The exact GodotType of the value stored in this Variant. 610 611 To check if a Variant is a specific GodotType, use `isType` instead to 612 account for inheritance. 613 +/ 614 GodotType exactType() const 615 { 616 if(GodotObject o = this.as!GodotObject) 617 { 618 if(Ref!Script s = o.getScript().as!Script) return GodotType(s); 619 else return GodotType(BuiltInClass(o.getClass())); 620 } 621 else return GodotType(this.type); 622 } 623 } 624