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 }