1 module godot.api.reference; 2 3 import std.meta, std.traits; // std.typecons; 4 import std.algorithm : swap; 5 6 import godot, godot.abi; 7 import godot.refcounted, godot.object; 8 import godot.api.traits, godot.api.script; 9 10 /// Ref-counted container for Reference types 11 struct Ref(T) { 12 static assert(extends!(T, RefCounted), "Ref may only be used with Reference-derived classes. Other Godot classes are not reference-counted."); 13 static assert(!is(T == const), "Ref cannot contain a const Reference"); 14 //@nogc nothrow: 15 16 static if (isGodotBaseClass!T) { 17 package(godot) T _reference; 18 alias _self = _reference; 19 } else { 20 package(godot) T _self; 21 pragma(inline, true) 22 package(godot) GodotClass!T _reference() { 23 // TODO: check again when object casts will be fixed 24 // for now it has to be this way or _self will point at random garbage 25 // causing unexpected failures, see test project "RefTest t = n;" aroung line 356 26 return (_self) ? *cast(typeof(return)*) &_self : GodotClass!T.init; 27 } 28 } 29 30 /++ 31 Returns the reference without allowing it to escape the calling scope. 32 33 TODO: dip1000 34 +/ 35 T refPayload() const { 36 return cast() _self; 37 } 38 39 alias refPayload this; 40 41 ref Ref opAssign(T other) { 42 if (_self.getGodotObject == other.getGodotObject) 43 return this; 44 unref(); 45 _self = other; 46 if (_self) 47 _reference.reference(); 48 return this; 49 } 50 51 ref Ref opAssign(R)(ref R other) if (is(R : Ref!U, U) && extends!(T, U)) { 52 opAssign(other._self); 53 return this; 54 } 55 56 ref Ref opAssign(R)(R other) if (is(R : Ref!U, U) && extends!(T, U)) { 57 swap(_self, other); 58 return this; 59 } 60 61 void unref() { 62 if (_self && _reference.unreference()) { 63 _godot_api.object_destroy(&_reference._godot_object); 64 } 65 _self = T.init; 66 } 67 68 Ref!U as(U)() if (isGodotClass!U && !is(U == GodotObject)) { 69 // the only non-Reference this can possibly be is Object, so no need to account for non-Refs 70 static assert(extends!(U, T) || extends!(T, U), 71 U.stringof ~ " is not polymorphic to " ~ T.stringof); 72 Ref!U ret = _self.as!U; 73 return ret; 74 } 75 76 template as(R) if (is(R : Ref!U, U) && isGodotClass!(NonRef!R)) { 77 alias as = as!(NonRef!R); 78 } 79 80 GodotObject as(R)() if (is(Unqual!R == GodotObject)) { 81 return _reference; 82 } 83 84 template opCast(R) if (isGodotClass!(NonRef!R)) { 85 alias opCast = as!R; 86 } 87 88 pragma(inline, true) 89 bool opEquals(R)(in auto ref R other) const { 90 return _self.getGDExtensionObject!T == other.getGDExtensionObject!T; 91 } 92 93 pragma(inline, true) 94 bool isValid() const { 95 return _self.getGodotObject != GodotClass!T.init; 96 } 97 98 alias opCast(T : bool) = isValid; 99 pragma(inline, true) 100 bool isNull() const { 101 return _self.getGodotObject == GodotClass!T.init; 102 } 103 104 this(this) { 105 if (_self) 106 _reference.reference(); 107 } 108 109 /++ 110 Construct from other reference 111 +/ 112 this(T other) { 113 _self = other; 114 if (_self) 115 _reference.reference(); 116 } 117 118 this(R)(ref R other) if (is(R : Ref!U, U) && extends!(T, U)) { 119 _self = other._self; 120 if (_self) 121 _reference.reference(); 122 } 123 124 this(R)(R other) if (is(R : Ref!U, U) && extends!(T, U)) { 125 swap(_self, other); 126 } 127 128 ~this() { 129 //unref(); 130 } 131 } 132 133 /++ 134 Create a Ref from a pointer without incrementing refcount. 135 +/ 136 package(godot) RefOrT!T refOrT(T)(T instance) { 137 static if (extends!(T, RefCounted)) { 138 Ref!T ret = void; 139 ret._self = instance; 140 return ret; 141 } else 142 return instance; 143 } 144 145 /++ 146 Create a Ref from a pointer and increment refcount. 147 +/ 148 package(godot) RefOrT!T refOrTInc(T)(T instance) { 149 static if (extends!(T, RefCounted)) { 150 Ref!T ret = void; 151 ret._self = instance; 152 if (ret._self) 153 ret._reference.reference(); 154 return ret; 155 } else 156 return instance; 157 }