1 /++
2 Templates for binding Godot C++ classes to use from D
3 
4 The binding generator will implement these templates for the classes in Godot's
5 API JSON.
6 +/
7 module godot.api.bind;
8 
9 import std.meta, std.traits;
10 import std.conv : text;
11 
12 import godot, godot.abi;
13 public import godot.refcounted;
14 import godot.api.traits;
15 
16 /// Type to mark varargs GodotMethod.
17 struct GodotVarArgs {
18 
19 }
20 
21 package(godot) struct MethodHash {
22     uint hash;
23 }
24 
25 package(godot) struct GodotName {
26     string name;
27 }
28 
29 /++
30 Definition of a method from API JSON.
31 +/
32 struct GodotMethod(Return, Args...) {
33     GDNativeMethodBindPtr mb; /// MethodBind for ptrcalls
34     String name; /// String name from Godot (snake_case, not always valid D)
35 
36     static if (Args.length)
37         enum bool hasVarArgs = is(Args[$ - 1] : GodotVarArgs);
38     else
39         enum bool hasVarArgs = false;
40 
41     /+package(godot)+/
42     void bind(in char* className, in char* methodName, in GDNativeInt hash = 0) {
43         if (mb)
44             return;
45         mb = _godot_api.classdb_get_method_bind(className, methodName, hash);
46         name = String(methodName);
47     }
48 
49     /+package(godot)+/
50     void bind(in GDNativeVariantType type, in char* methodName, in GDNativeInt hash = 0) {
51         if (mb)
52             return;
53         mb = _godot_api.variant_get_ptr_builtin_method(type, methodName, hash);
54         name = String(methodName);
55     }
56 }
57 
58 struct GodotConstructor(Return, Args...) {
59     GDNativePtrConstructor mb; /// MethodBind for ptrcalls
60 
61     /+package(godot)+/
62     void bind(in GDNativeVariantType type, in int index) {
63         if (mb)
64             return;
65         mb = _godot_api.variant_get_ptr_constructor(type, index);
66     }
67 }
68 
69 /++
70 Raw Method call helper
71 +/
72 Return callBuiltinMethod(Return, Args...)(in GDNativePtrBuiltInMethod method, GDNativeTypePtr obj, Args args) {
73     static if (!is(Return == void))
74         Return ret = void;
75     else
76         typeof(null) ret = null;
77 
78     GDNativeTypePtr[Args.length + 1] _args;
79     foreach (i, a; args) {
80         _args[i] = &a;
81     }
82 
83     method(obj, _args.ptr, &ret, _args.length);
84     static if (!is(Return == void))
85         return ret;
86 }
87 
88 //@nogc nothrow 
89 pragma(inline, true)
90 package(godot) void checkClassBinding(C)() {
91     if (!C._classBindingInitialized) {
92         initializeClassBinding!C();
93     }
94 }
95 
96 // these have same order as in GDNativeVariantType
97 private immutable enum coreTypes = [
98         "Nil", "Bool", "Int", "Float", "String",
99 
100         "Vector2", "Vector2i", "Rect2", "Rect2i", "Vector3",
101         "Vector3i", "Transform2D", "Vector4", "Vector4i",
102         "Plane", "Quaternion", "AABB", "Basis", "Transform3D", "Projection",
103 
104         "Color", "StringName", "NodePath", "RID", "Object",
105         "Callable", "Signal", "Dictionary", "Array",
106 
107         "PackedByteArray", "PackedInt32Array", "PackedInt64Array",
108         "PackedFloat32Array",
109         "PackedFloat64Array", "PackedStringArray", "PackedVector2Array",
110         "PackedVector3Array",
111         "PackedColorArray",
112     ];
113 
114 //@nogc nothrow 
115 pragma(inline, false)
116 package(godot) void initializeClassBinding(C)() {
117     import std.algorithm;
118     import std.string : indexOf;
119 
120     synchronized {
121         if (!C._classBindingInitialized) {
122             static foreach (n; __traits(allMembers, C.GDNativeClassBinding)) {
123                 static if (n == "_singleton")
124                     C.GDNativeClassBinding._singleton = godot_object(
125                         _godot_api.global_get_singleton(
126                             cast(char*) C.GDNativeClassBinding._singletonName));
127                 else static if (n == "_singletonName") {
128                 } else {
129                     // core types require special registration for built-in types
130                     static if (coreTypes.canFind(C._GODOT_internal_name)) {
131                         static if (isInstanceOf!(GodotConstructor, __traits(getMember, C.GDNativeClassBinding, n))) {
132                             // binds constructor using GDNativeVariantType and index
133                             __traits(getMember, C.GDNativeClassBinding, n).bind(
134                                 cast(int) coreTypes.countUntil(C._GODOT_internal_name),
135                                 to!int(getUDAs!(mixin("C.GDNativeClassBinding." ~ n), GodotName)[0].name[$ - 2 .. $ - 1]), // get last number from name in form of "_new_2"
136                                 
137                             );
138                         } else {
139                             // binds native built-in method
140                             __traits(getMember, C.GDNativeClassBinding, n).bind(
141                                 cast(int) coreTypes.countUntil(C._GODOT_internal_name),
142                                 getUDAs!(mixin("C.GDNativeClassBinding." ~ n), GodotName)[0].name,
143                                 getUDAs!(mixin("C.GDNativeClassBinding." ~ n), MethodHash)[0].hash,
144                             );
145                         }
146                     } else {
147                         //enum immutable(char*) cn = C._GODOT_internal_name;
148                         __traits(getMember, C.GDNativeClassBinding, n).bind(
149                             C._GODOT_internal_name,
150                             getUDAs!(__traits(getMember, C.GDNativeClassBinding, n), GodotName)[0].name,
151                             0 //getUDAs!(__traits(getMember, C.GDNativeClassBinding, n), MethodHash)[0].hash,
152                         
153                         );
154                     }
155                 }
156             }
157             C._classBindingInitialized = true;
158         }
159     }
160 }
161 
162 enum bool needsConversion(Src, Dest) = !isGodotClass!Dest && !is(Src : Dest);
163 
164 /// temporary var if conversion is needed
165 template tempType(Src, Dest) {
166     static if (needsConversion!(Src, Dest))
167         alias tempType = Dest;
168     else
169         alias tempType = void[0];
170 }
171 
172 /++
173 Direct pointer call through MethodBind.
174 +/
175 RefOrT!Return ptrcall(Return, MB, Args...)(MB method, in godot_object self, Args args)
176 in {
177     debug if (self.ptr is null) {
178         auto utf8 = (String("Method ") ~ method.name ~ String(" called on null reference")).utf8;
179         auto msg = cast(char[]) utf8.data[0 .. utf8.length];
180         assert(0, msg);
181     }
182 }
183 do {
184     import std.typecons;
185     import std.range : iota;
186 
187     alias MBArgs = TemplateArgsOf!(MB)[1 .. $];
188     static assert(Args.length == MBArgs.length);
189 
190     static if (Args.length != 0) {
191         alias _iota = aliasSeqOf!(iota(Args.length));
192         alias _tempType(size_t i) = tempType!(Args[i], MBArgs[i]);
193         const(void)*[Args.length] aarr = void;
194 
195         Tuple!(staticMap!(_tempType, _iota)) temp = void;
196     }
197     foreach (ai, A; Args) {
198         static if (isGodotClass!A) {
199             static assert(is(Unqual!A : MBArgs[ai]) || staticIndexOf!(
200                     MBArgs[ai], GodotClass!A.GodotClass) != -1, "method" ~
201                     " argument " ~ ai.text ~ " of type " ~ A.stringof ~
202                     " does not inherit parameter type " ~ MBArgs[ai].stringof);
203             aarr[ai] = getGDNativeObject(args[ai]).ptr;
204         } else static if (!needsConversion!(Args[ai], MBArgs[ai])) {
205             aarr[ai] = cast(const(void)*)(&args[ai]);
206         } else // needs conversion
207         {
208             static assert(is(typeof(MBArgs[ai](args[ai]))), "method" ~
209                     " argument " ~ ai.text ~ " of type " ~ A.stringof ~
210                     " cannot be converted to parameter type " ~ MBArgs[ai].stringof);
211 
212             import std.conv : emplace;
213 
214             emplace(&temp[ai], args[ai]);
215             aarr[ai] = cast(const(void)*)(&temp[ai]);
216         }
217     }
218     static if (!is(Return : void))
219         RefOrT!Return r = godotDefaultInit!(RefOrT!Return);
220 
221     static if (is(Return : void))
222         alias rptr = Alias!null;
223     else
224         void* rptr = cast(void*)&r;
225 
226     static if (Args.length == 0)
227         alias aptr = Alias!null;
228     else
229         const(void)** aptr = aarr.ptr;
230 
231     _godot_api.object_method_bind_ptrcall(method.mb, cast(GDNativeObjectPtr) self.ptr, aptr, rptr);
232     static if (!is(Return : void))
233         return r;
234 }
235 
236 /++
237 Variant call, for virtual and vararg methods.
238 
239 Forwards to `callv`, but does compile-time type check of args other than varargs.
240 +/
241 Return callv(MB, Return, Args...)(MB method, godot_object self, Args args)
242 in {
243     import std.experimental.allocator, std.experimental.allocator.mallocator;
244 
245     debug if (self.ptr is null) {
246         CharString utf8 = (String("Method ") ~ method.name ~ String(" called on null reference"))
247             .utf8;
248         auto msg = utf8.data;
249         assert(0, msg); // leak msg; Error is unrecoverable
250     }
251 }
252 do {
253     alias MBArgs = TemplateArgsOf!(MB)[1 .. $];
254 
255     import godot.object;
256 
257     GodotObject o = void;
258     o._godot_object = self;
259 
260     Array a = Array.make();
261     static if (Args.length != 0)
262         a.resize(cast(int) Args.length);
263     foreach (ai, A; Args) {
264         static if (is(MBArgs[$ - 1] : GodotVarArgs) && ai >= MBArgs.length - 1) {
265             // do nothing
266         } else {
267             static assert(ai < MBArgs.length, "Too many arguments");
268             static assert(is(A : MBArgs[ai]) || isImplicitlyConvertible!(A, MBArgs[ai]),
269                 "method" ~ " argument " ~ ai.text ~ " of type " ~ A.stringof ~
270                     " cannot be converted to parameter type " ~ MBArgs[ai].stringof);
271         }
272         a[ai] = args[ai];
273     }
274 
275     Variant r = o.callv(method.name, a);
276     return r.as!Return;
277 }
278 
279 package(godot)
280 mixin template baseCasts() {
281     private import godot.api.reference, godot.api.traits : RefOrT, NonRef;
282 
283     inout(To) as(To)() inout if (isGodotBaseClass!To) {
284         static if (extends!(typeof(this), To))
285             return cast(inout) To(cast() _godot_object);
286         else static if (extends!(To, typeof(this))) {
287             if (_godot_object.ptr is null)
288                 return typeof(return).init;
289             String c = String(To._GODOT_internal_name);
290             if (isClass(c))
291                 return inout(To)(_godot_object);
292             return typeof(return).init;
293         } else
294             static assert(0, To.stringof ~ " is not polymorphic to "
295                     ~ typeof(this).stringof);
296     }
297 
298     inout(ToRef) as(ToRef)() inout 
299             if (is(ToRef : Ref!To, To) && extends!(To, RefCounted)) {
300         import std.traits : TemplateArgsOf, Unqual;
301 
302         ToRef ret = cast() as!(Unqual!(TemplateArgsOf!ToRef[0]));
303         return cast(inout) ret;
304     }
305 
306     template opCast(To) if (isGodotBaseClass!To) {
307         alias opCast = as!To;
308     }
309 
310     template opCast(To) if (extendsGodotBaseClass!To) {
311         alias opCast = as!To;
312     }
313 
314     template opCast(ToRef) if (is(ToRef : Ref!To, To) && extends!(To, RefCounted)) {
315         alias opCast = as!ToRef;
316     }
317     // void* cast for passing this type to ptrcalls
318     package(godot) void* opCast(T : void*)() const {
319         return cast(void*) _godot_object.ptr;
320     }
321     // strip const, because the C API sometimes expects a non-const godot_object
322     godot_object opCast(T : godot_object)() const {
323         return cast(godot_object) _godot_object;
324     }
325     // implicit conversion to bool like D class references
326     bool opCast(T : bool)() const {
327         return _godot_object.ptr !is null;
328     }
329 }