1 /** 2 The most important data type in Godot. 3 4 Copyright: 5 Copyright (c) 2007 Juan Linietsky, Ariel Manzur. 6 Copyright (c) 2014 Godot Engine contributors (cf. AUTHORS.md) 7 Copyright (c) 2017 Godot-D contributors 8 Copyright (c) 2022 Godot-DLang contributors 9 10 License: $(LINK2 https://opensource.org/licenses/MIT, MIT License) 11 12 13 */ 14 module godot.variant; 15 16 import godot.abi; 17 import godot; 18 import godot.object; 19 import godot.api.traits; 20 import godot.api.reference; 21 import godot.script; 22 import godot.api.types; 23 24 import std.meta, std.traits; 25 import std.conv : text; 26 import std.range; 27 28 // for tests 29 import godot.node; 30 import godot.resource; 31 32 // FIXME ABI type should probably have its own `version`... 33 version (X86_64) { 34 version (DigitalMars) { 35 version (linux) version = GodotSystemV; 36 version (OSX) version = GodotSystemV; 37 version (Posix) version = GodotSystemV; 38 } 39 } 40 41 /// User-defined Variant conversions. 42 /// For structs and classes, constructors and member functions can also be used. 43 unittest { 44 struct A { 45 } 46 47 static assert(!Variant.compatible!A); 48 49 struct B { 50 } 51 52 B to(T : B)(Variant v) { 53 return B(); 54 } 55 56 int to(T : Variant)(B b) { 57 return 1; 58 } 59 60 static assert(Variant.compatible!B); 61 62 struct C { 63 this(Variant v) { 64 } 65 66 Variant to(T : Variant)() { 67 return Variant(1); 68 } 69 } 70 71 static assert(Variant.compatible!C); 72 73 B b; 74 C c; 75 76 Variant vb = b; 77 Variant vc = c; 78 79 b = vb.as!B; 80 c = vc.as!C; 81 } 82 83 /// 84 enum VariantType { 85 nil, 86 87 // atomic types 88 bool_, 89 int_, 90 float_, 91 string, 92 93 // math types 94 95 vector2, // 5 96 vector2i, 97 rect2, 98 rect2i, 99 vector3, 100 vector3i, // 10 101 transform2d, 102 vector4, 103 vector4i, 104 plane, 105 quaternion, 106 aabb, 107 basis, // 15 108 transform3d, 109 projection, 110 111 // misc types 112 color, 113 string_name, 114 node_path, 115 rid, 116 object, // 20 117 callable, 118 signal, 119 dictionary, 120 array, 121 122 // arrays 123 packed_byte_array, // 25 124 packed_int32_array, 125 packed_int64_array, 126 packed_float32_array, 127 packed_float64_array, 128 packed_string_array, // 30 129 packed_vector2_array, 130 packed_vector3_array, 131 packed_color_array, 132 } 133 134 /** 135 Godot's tagged union type. 136 137 Primitives, Godot core types, and `GodotObject`-derived classes can be stored in 138 a Variant. Other user-defined D types can be made compatible with Variant by 139 defining `to!CustomType(Variant)` and `to!Variant(CustomType)` functions. 140 141 Properties and method arguments/returns are passed between Godot and D through 142 Variant, so these must use Variant-compatible types. 143 */ 144 struct Variant { 145 package(godot) godot_variant _godot_variant; 146 147 // having it inside variant creates annoying recursive alias issue 148 alias Type = VariantType; 149 150 /// GDExtension type that gets passed to the C functions 151 // NOTE: godot 4 now uses default int as int32 and double precision by default 152 // TODO: verify this 153 alias InternalType = AliasSeq!( 154 typeof(null), 155 156 godot_bool, 157 int, 158 double, 159 godot_string, 160 161 godot_vector2, // 5 162 godot_vector2i, 163 godot_rect2, 164 godot_rect2i, 165 godot_vector3, 166 godot_vector3i, // 10 167 godot_transform2d, 168 godot_vector4, 169 godot_vector4i, 170 godot_plane, 171 godot_quaternion, 172 godot_aabb, 173 godot_basis, // 15 174 godot_transform3d, 175 godot_projection, 176 177 godot_color, 178 godot_string, //godot_string_name 179 godot_node_path, 180 godot_rid, // 20 181 godot_object, 182 godot_callable, 183 godot_signal, 184 godot_dictionary, 185 godot_array, // 25 186 187 godot_packed_byte_array, 188 godot_packed_int32_array, 189 godot_packed_int64_array, 190 godot_packed_float32_array, 191 godot_packed_float64_array, // 30 192 godot_packed_string_array, 193 godot_packed_vector2_array, 194 godot_packed_vector3_array, 195 godot_packed_color_array, 196 ); 197 198 /// D type that this Variant implementation uses 199 alias DType = AliasSeq!( 200 typeof(null), 201 202 bool, 203 long, 204 double, 205 String, 206 207 Vector2, // 5 208 Vector2i, 209 Rect2, 210 Rect2i, 211 Vector3, 212 Vector3i, // 10 213 Transform2D, 214 Vector4, 215 Vector4i, 216 Plane, 217 Quaternion, 218 AABB, 219 Basis, // 15 220 Transform3D, 221 Projection,// misc types 222 Color, 223 StringName, 224 NodePath, 225 RID, // 20 226 GodotObject, 227 GodotCallable, 228 GodotSignal, 229 Dictionary, 230 Array, // 25 231 232 // arrays 233 PackedByteArray, 234 PackedInt32Array, 235 PackedInt64Array, 236 PackedFloat32Array, 237 PackedFloat64Array, // 30 238 PackedStringArray, 239 PackedVector2Array, 240 PackedVector3Array, 241 PackedColorArray, 242 ); 243 244 /// 245 enum Operator { 246 //comparation 247 equal, 248 notEqual, 249 less, 250 lessEqual, 251 greater, 252 greaterEqual, 253 254 //mathematic 255 add, 256 substract, 257 multiply, 258 divide, 259 negate, 260 positive, 261 modulus, 262 power, 263 //stringConcat, 264 265 //bitwise 266 shiftLeft, 267 shiftRight, 268 bitAnd, 269 bitOr, 270 bitXor, 271 bitNegate, 272 273 //logic 274 and, 275 or, 276 xor, 277 not, 278 279 //containment 280 in_ 281 } 282 283 private enum bool implicit(Src, Dest) = is(Src : Dest) || isImplicitlyConvertible!(Src, Dest); 284 285 private static GodotObject objectToGodot(T)(T o) { 286 return o.getGodotObject; 287 } 288 289 // Conversions for non-core types provided by Variant. 290 // Lower priority than user-defined `to` functions, to allow overriding 291 // default blanket implementations. 292 private alias internalAs(T) = (Variant v) => v.as!T; 293 private enum bool hasInternalAs(T) = __traits(compiles, internalAs!T); 294 private alias internalFrom(T) = (T t) => Variant.from(t); 295 private enum bool hasInternalFrom(T) = __traits(compiles, internalFrom!T); 296 297 /// 298 T as(T)() const if (isStaticArray!T && compatibleFromGodot!(ElementType!T)) { 299 return as!Array.as!T; 300 } 301 302 /// 303 T as(T)() const if (is(T : TypedArray!U, U...) && !is(U == Array)) { 304 return T(as!Array); 305 } 306 307 T as(T : void*)() { 308 return cast(T)&_godot_variant; 309 } 310 311 /// 312 T as(T)() const 313 if ((isGodotClass!T && !is(T == GodotObject)) || is(T : Ref!U, U)) { 314 GodotObject o = cast()(as!GodotObject); 315 return o.as!T; 316 } 317 318 /// 319 static Array from(T)(T t) 320 if ((isForwardRange!T || isStaticArray!T) && compatibleToGodot!(ElementType!T)) { 321 return Array.from(t); 322 } 323 324 /// 325 GodotType as(T : GodotType)() const { 326 if (type == Type.object) { 327 Ref!Script s = as!Script; 328 if (s) 329 return GodotType(s); 330 else 331 return GodotType.init; 332 } else if (type == Type..string) 333 return GodotType(BuiltInClass(as!String)); 334 else if (type == Type.int_) 335 return GodotType(cast(Variant.Type)(as!int)); 336 else 337 return GodotType.init; 338 } 339 340 /// 341 static Variant from(T : GodotType)(T t) { 342 import std.sumtype : match; 343 344 Variant ret; 345 t.match!( 346 (Variant.Type t) { ret = cast(int) t; }, 347 (BuiltInClass c) { ret = c.name; }, 348 (Ref!Script s) { ret = s; } 349 ); 350 return ret; 351 } 352 353 // TODO: fix me 354 /* 355 static assert(hasInternalAs!Node, internalAs!Node); 356 static assert(hasInternalAs!(Ref!Resource), internalAs!(Ref!Resource)); 357 static assert(!hasInternalAs!Object); // `directlyCompatible` types not handled by internalAs 358 static assert(hasInternalAs!(int[4]), internalAs!(int[4])); 359 static assert(hasInternalFrom!(int[4]), internalFrom!(int[4])); 360 static assert(!hasInternalAs!(int[])); 361 static assert(hasInternalFrom!(int[]), internalFrom!(int[])); 362 static assert(hasInternalAs!GodotType, internalAs!GodotType); 363 static assert(hasInternalFrom!GodotType, internalFrom!GodotType); 364 static assert(compatible!GodotType); 365 */ 366 367 private template getToVariantFunction(T) { 368 mixin("import " ~ moduleName!T ~ ";"); 369 alias getToVariantFunction = (T t) { Variant v = t.to!Variant; return v; }; 370 } 371 372 enum bool hasToVariantFunction(T) = __traits(compiles, getToVariantFunction!T); 373 374 private template getVariantConstructor(T) { 375 alias getVariantConstructor = (Variant v) => T(v); 376 } 377 378 enum bool hasVariantConstructor(T) = __traits(compiles, getVariantConstructor!T); 379 380 template getFromVariantFunction(T) { 381 mixin("import " ~ moduleName!T ~ ";"); 382 alias getFromVariantFunction = (Variant v) { T ret = v.to!T; return ret; }; 383 } 384 385 enum bool hasFromVariantFunction(T) = __traits(compiles, getFromVariantFunction!T); 386 387 /// function to convert T to an equivalent Godot type 388 template conversionToGodot(T) { 389 static if (isGodotClass!T) 390 alias conversionToGodot = objectToGodot!T; 391 else static if (is(T : GodotStringLiteral!s, string s)) 392 alias conversionToGodot = (T t) => t.str(); 393 else static if (is(T : Ref!U, U)) 394 alias conversionToGodot = objectToGodot!U; 395 else static if (isIntegral!T) 396 alias conversionToGodot = (T t) => cast(long) t; 397 else static if (isFloatingPoint!T) 398 alias conversionToGodot = (T t) => cast(double) t; 399 else static if (implicit!(T, const(char)[]) || implicit!(T, const(char)*)) 400 alias conversionToGodot = (T t) => String(t); 401 else static if (hasToVariantFunction!T) { 402 alias conversionToGodot = getToVariantFunction!T; 403 } else static if (hasInternalFrom!T) 404 alias conversionToGodot = internalFrom!T; 405 else 406 alias conversionToGodot = void; // none 407 } 408 409 enum bool convertsToGodot(T) = isCallable!(conversionToGodot!T); 410 alias conversionToGodotType(T) = Unqual!(ReturnType!(conversionToGodot!T)); 411 412 /// function to convert a Godot-compatible type to T 413 template conversionFromGodot(T) { 414 static if (isIntegral!T) 415 alias conversionFromGodot = (long v) => cast(T) v; 416 else static if (isFloatingPoint!T) 417 alias conversionFromGodot = (double v) => cast(T) v; 418 else static if (hasVariantConstructor!T) { 419 alias conversionFromGodot = getVariantConstructor!T; 420 } else static if (hasFromVariantFunction!T) { 421 alias conversionFromGodot = getFromVariantFunction!T; 422 } else 423 alias conversionFromGodot = void; 424 } 425 426 enum bool convertsFromGodot(T) = isCallable!(conversionFromGodot!T); 427 alias conversionFromGodotType(T) = Unqual!(Parameters!(conversionFromGodot!T)[0]); 428 429 enum bool directlyCompatible(T) = staticIndexOf!(Unqual!T, DType) != -1; 430 template compatibleToGodot(T) { 431 static if (directlyCompatible!T) 432 enum bool compatibleToGodot = true; 433 else 434 enum bool compatibleToGodot = convertsToGodot!T; 435 } 436 437 template compatibleFromGodot(T) { 438 static if (directlyCompatible!T) 439 enum bool compatibleFromGodot = true; 440 else static if (hasInternalAs!T) 441 enum bool compatibleFromGodot = true; 442 else 443 enum bool compatibleFromGodot = convertsFromGodot!T; 444 } 445 446 enum bool compatible(R) = compatibleToGodot!(R) && compatibleFromGodot!(R); 447 448 /// All target Variant.Types that T could implicitly convert to, as indices 449 private template implicitTargetIndices(T) { 450 private enum bool _implicit(size_t di) = implicit!(T, DType[di]); 451 alias implicitTargetIndices = Filter!(_implicit, aliasSeqOf!(iota(DType.length))); 452 } 453 454 /++ 455 Get the Variant.Type of a compatible D type. Incompatible types return nil. 456 +/ 457 public template variantTypeOf(T) { 458 import std.traits, godot; 459 460 static if (directlyCompatible!T) { 461 enum Type variantTypeOf = EnumMembers!Type[staticIndexOf!(Unqual!T, DType)]; 462 } else static if (convertsToGodot!T) { 463 static if (is(conversionToGodotType!T : Variant)) 464 enum Type variantTypeOf = Type.nil; 465 else 466 enum Type variantTypeOf = EnumMembers!Type[staticIndexOf!( 467 conversionToGodotType!T, DType)]; 468 } else 469 enum Type variantTypeOf = Type.nil; // so the template always returns a Type 470 } 471 472 /// 473 R as(R)() const 474 if (!is(R == Variant) && !is(R == typeof(null)) && (convertsFromGodot!R || directlyCompatible!R)) { 475 static if (directlyCompatible!R) 476 enum VarType = variantTypeOf!R; 477 else static if (is(conversionFromGodotType!R : Variant)) 478 enum VarType = Type.nil; 479 else 480 enum VarType = EnumMembers!Type[staticIndexOf!(conversionFromGodotType!R, DType)]; 481 482 static if (VarType == Type.nil) { 483 return conversionFromGodot!R(this); 484 } else static if (is(Unqual!R == String)) { 485 static if (is(Unqual!R == NodePath)) 486 godot_node_path str; 487 else 488 godot_string str; 489 _godot_api.variant_stringify(&_godot_variant, cast(void*)&str); 490 return R(str); 491 } else { 492 DType[VarType] ret = void; 493 //*cast(InternalType[VarType]*)&ret = mixin("_godot_api.variant_as_"~FunctionAs!VarType~"(&_godot_variant)"); 494 495 // this gives wrong result, try the other way around 496 //auto fn = _godot_api.get_variant_from_type_constructor(VarType); 497 //fn(cast(GDExtensionVariantPtr) &_godot_variant, &ret); 498 499 // special case such as calling function with null optional parameter 500 if (_godot_variant._opaque.ptr is null) { 501 return R.init; 502 } 503 import core.stdc.string; 504 memset(&ret, 0, ret.sizeof); 505 506 auto fn = _godot_api.get_variant_to_type_constructor(cast(GDExtensionVariantType) VarType); 507 fn(cast(void*)&ret, cast(void*)&_godot_variant); 508 509 static if (directlyCompatible!R) 510 return ret; 511 else { 512 return conversionFromGodot!R(ret); 513 } 514 } 515 } 516 517 this(R)(auto ref R input) if (!is(R : Variant) && !is(R : typeof(null))) { 518 static assert(compatibleToGodot!R, R.stringof ~ " isn't compatible with Variant."); 519 enum VarType = variantTypeOf!R; 520 521 static if (VarType == Type.nil) { 522 this = conversionToGodot!R(input); 523 } else { 524 //mixin("auto Fn = _godot_api.variant_new_"~FunctionNew!VarType~";"); 525 // auto Fn = _godot_api.get_variant_from_type_constructor(VarType); 526 auto Fn = _godot_api.get_variant_from_type_constructor(cast(GDExtensionVariantType) VarType); 527 alias PassType = Parameters!Fn[1]; // second param is the value 528 529 alias IT = InternalType[VarType]; 530 531 // handle explicit conversions 532 static if (directlyCompatible!R) 533 alias inputConv = input; 534 else 535 auto inputConv = conversionToGodot!R(input); 536 537 static if (is(IT == Unqual!PassType)) 538 Fn(&_godot_variant, cast(IT) inputConv); // value 539 else 540 Fn(&_godot_variant, cast(IT*)&inputConv); // pointer 541 } 542 } 543 544 pragma(inline, true) 545 void opAssign(T)(in auto ref T input) 546 if (!is(T : Variant) && !is(T : typeof(null)) && !is(Unqual!T : void*)) { 547 import std.conv : emplace; 548 549 _godot_api.variant_destroy(&_godot_variant); 550 static if (is(T : TypedArray!Args, Args...)) { 551 // hacky way, for some reasons 'alias this' was ignored 552 emplace!(Variant)(&this, input._array); 553 } else { 554 emplace!(Variant)(&this, input); 555 } 556 } 557 558 // internal use only, but can be useful for users who knows what they are doing 559 // used in few cases only, audioeffect.process() as a buffer is one example 560 pragma(inline, true) 561 package(godot) void opAssign(const void* input) { 562 // can it be messed up by alignment? 563 _godot_variant = *cast(godot_variant*) input; 564 } 565 566 static assert(allSatisfy!(compatible, DType)); 567 static assert(!compatible!Object); // D Object 568 569 static assert(directlyCompatible!GodotObject); 570 static assert(directlyCompatible!(const(GodotObject))); 571 static assert(!directlyCompatible!Node); 572 // TODO: fix me 573 //static assert(compatibleFromGodot!Node); 574 static assert(compatibleToGodot!Node); 575 // TODO: fix me 576 //static assert(compatibleFromGodot!(const(Node))); 577 static assert(compatibleToGodot!(const(Node))); 578 static assert(!directlyCompatible!(Ref!Resource)); 579 // TODO: fix me 580 //static assert(compatibleFromGodot!(Ref!Resource)); 581 static assert(compatibleToGodot!(Ref!Resource)); 582 // TODO: fix me 583 //static assert(compatibleFromGodot!(const(Ref!Resource))); 584 static assert(compatibleToGodot!(const(Ref!Resource))); 585 586 private template FunctionAs(Type type) { 587 private enum string name_ = text(type); 588 private enum string FunctionAs = (name_[$ - 1] == '_') ? (name_[0 .. $ - 1]) : name_; 589 } 590 591 private template FunctionNew(Type type) { 592 private enum string name_ = text(type); 593 private enum string FunctionNew = (name_[$ - 1] == '_') ? (name_[0 .. $ - 1]) : name_; 594 } 595 596 //@nogc nothrow: 597 this(this) { 598 godot_variant other = _godot_variant; // source Variant still owns this 599 _godot_api.variant_new_copy(&_godot_variant, &other); 600 } 601 602 static Variant nil() { 603 Variant v = void; 604 _godot_api.variant_new_nil(&v._godot_variant); 605 return v; 606 } 607 608 this(in ref Variant other) { 609 _godot_api.variant_new_copy(&_godot_variant, &other._godot_variant); 610 } 611 612 this(T : typeof(null))(in T nil) { 613 _godot_api.variant_new_nil(&_godot_variant); 614 } 615 616 ~this() { 617 // TODO: need to check this, causes broken values after several Variant to variant assignments 618 _godot_api.variant_destroy(&_godot_variant); 619 } 620 621 Type type() const { 622 return cast(Type) _godot_api.variant_get_type(&_godot_variant); 623 } 624 625 inout(T) as(T : Variant)() inout { 626 return this; 627 } 628 629 pragma(inline, true) 630 void opAssign(T : typeof(null))(in T nil) { 631 _godot_api.variant_destroy(&_godot_variant); 632 _godot_api.variant_new_nil(&_godot_variant); 633 } 634 635 pragma(inline, true) 636 void opAssign(T : Variant)(in T other) { 637 _godot_api.variant_destroy(&_godot_variant); 638 _godot_api.variant_new_copy(&_godot_variant, &other._godot_variant); 639 } 640 641 bool opEquals(in ref Variant other) const { 642 Variant ret; 643 bool valid; 644 evaluate(GDEXTENSION_VARIANT_OP_EQUAL, this, other, ret, valid); 645 return ret.as!bool; 646 //return cast(bool)_godot_api.variant_operator_equal(&_godot_variant, &other._godot_variant); 647 } 648 649 private void evaluate(int op, ref const Variant a, ref const Variant b, ref Variant ret, ref bool isValid) const { 650 GDExtensionBool res; 651 _godot_api.variant_evaluate(cast(GDExtensionVariantOperator) op, &a._godot_variant, &b._godot_variant, &ret._godot_variant, &res); 652 isValid = !!res; 653 } 654 655 int opCmp(in ref Variant other) const { 656 Variant res; 657 bool valid; 658 evaluate(GDEXTENSION_VARIANT_OP_EQUAL, this, other, res, valid); 659 if (res.as!bool) 660 return 0; 661 evaluate(GDEXTENSION_VARIANT_OP_LESS, this, other, res, valid); 662 return res.as!bool ? -1 : 1; 663 //if(_godot_api.variant_operator_equal(&_godot_variant, &other._godot_variant)) 664 // return 0; 665 //return _godot_api.variant_operator_less(&_godot_variant, &other._godot_variant)? 666 // -1 : 1; 667 } 668 669 bool booleanize() const { 670 return cast(bool) _godot_api.variant_booleanize(&_godot_variant); 671 } 672 673 auto toString() const { 674 String str = as!String; 675 return str.data; 676 } 677 678 /// Is this Variant of the specified `type` or of a subclass of `type`? 679 bool isType(GodotType type) const { 680 import std.sumtype : match; 681 682 return type.match!( 683 (Ref!Script script) { 684 GodotObject o = this.as!GodotObject; 685 if (o == null) 686 return false; 687 return script.instanceHas(o); 688 }, 689 (BuiltInClass object) { 690 GodotObject o = this.as!GodotObject; 691 if (o == null) 692 return false; 693 // return o.isClass(object.name); 694 return o.isClass(toDString(object.name)); 695 }, 696 (Type vt) => this.type == vt 697 ); 698 } 699 700 /++ 701 The exact GodotType of the value stored in this Variant. 702 703 To check if a Variant is a specific GodotType, use `isType` instead to 704 account for inheritance. 705 +/ 706 GodotType exactType() const { 707 if (GodotObject o = this.as!GodotObject) { 708 if (Ref!Script s = o.getScript().as!Script) 709 return GodotType(s); 710 else 711 return GodotType(BuiltInClass(o.getClass())); 712 } else 713 return GodotType(this.type); 714 } 715 }