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.d.bind;
8 
9 import std.meta, std.traits;
10 import std.conv : text;
11 
12 import godot.core, godot.c;
13 import godot.d.traits;
14 
15 /// Type to mark varargs GodotMethod.
16 struct GodotVarArgs
17 {
18 	
19 }
20 
21 package(godot) struct GodotName { string name; }
22 
23 /++
24 Definition of a method from API JSON.
25 +/
26 struct GodotMethod(Return, Args...)
27 {
28 	godot_method_bind* mb; /// MethodBind for ptrcalls
29 	String name; /// String name from Godot (snake_case, not always valid D)
30 	
31 	static if(Args.length) enum bool hasVarArgs = is(Args[$-1] : GodotVarArgs);
32 	else enum bool hasVarArgs = false;
33 	
34 	/+package(godot)+/ void bind(in char* className, in char* methodName)
35 	{
36 		if(mb) return;
37 		mb = _godot_api.godot_method_bind_get_method(className, methodName);
38 		name = String(methodName);
39 	}
40 }
41 
42 @nogc nothrow pragma(inline, true)
43 package(godot) void checkClassBinding(C)()
44 {
45 	if(!C._classBindingInitialized)
46 	{
47 		initializeClassBinding!C();
48 	}
49 }
50 
51 @nogc nothrow pragma(inline, false)
52 package(godot) void initializeClassBinding(C)()
53 {
54 	synchronized
55 	{
56 		if(!C._classBindingInitialized)
57 		{
58 			static foreach(n; __traits(allMembers, C.GDNativeClassBinding))
59 			{
60 				static if(n == "_singleton") C.GDNativeClassBinding._singleton = _godot_api
61 					.godot_global_get_singleton(cast(char*)C.GDNativeClassBinding._singletonName);
62 				else static if(n == "_singletonName"){}
63 				else
64 				{
65 					//enum immutable(char*) cn = C._GODOT_internal_name;
66 					mixin("C.GDNativeClassBinding."~n).bind(C._GODOT_internal_name,
67 						getUDAs!(mixin("C.GDNativeClassBinding."~n), GodotName)[0].name);
68 				}
69 			}
70 			C._classBindingInitialized = true;
71 		}
72 	}
73 }
74 
75 enum bool needsConversion(Src, Dest) = !isGodotClass!Dest && !is(Src : Dest);
76 
77 /// temporary var if conversion is needed
78 template tempType(Src, Dest)
79 {
80 	static if( needsConversion!(Src, Dest) ) alias tempType = Dest;
81 	else alias tempType = void[0];
82 }
83 
84 /++
85 Direct pointer call through MethodBind.
86 +/
87 RefOrT!Return ptrcall(Return, MB, Args...)(MB method, in godot_object self, Args args)
88 in
89 {
90 	import std.experimental.allocator, std.experimental.allocator.mallocator;
91 	debug if(self.ptr is null)
92 	{
93 		CharString utf8 = (String("Method ")~method.name~String(" called on null reference")).utf8;
94 		auto msg = utf8.data;
95 		assert(0, msg); // leak msg; Error is unrecoverable
96 	}
97 }
98 do
99 {
100 	import std.typecons;
101 	import std.range : iota;
102 	
103 	alias MBArgs = TemplateArgsOf!(MB)[1..$];
104 	static assert(Args.length == MBArgs.length);
105 	
106 	static if(Args.length != 0)
107 	{
108 		alias _iota = aliasSeqOf!(iota(Args.length));
109 		alias _tempType(size_t i) = tempType!(Args[i], MBArgs[i]);
110 		const(void)*[Args.length] aarr = void;
111 		
112 		Tuple!( staticMap!(_tempType, _iota) ) temp = void;
113 	}
114 	foreach(ai, A; Args)
115 	{
116 		static if(isGodotClass!A)
117 		{
118 			static assert(is(Unqual!A : MBArgs[ai]) || staticIndexOf!(
119 				MBArgs[ai], GodotClass!A.GodotClass) != -1, "method" ~
120 				" argument " ~ ai.text ~ " of type " ~ A.stringof ~
121 				" does not inherit parameter type " ~ MBArgs[ai].stringof);
122 			aarr[ai] = getGDNativeObject(args[ai]).ptr;
123 		}
124 		else static if( !needsConversion!(Args[ai], MBArgs[ai]) )
125 		{
126 			aarr[ai] = cast(const(void)*)(&args[ai]);
127 		}
128 		else // needs conversion
129 		{
130 			static assert(is(typeof(MBArgs[ai](args[ai]))), "method" ~
131 				" argument " ~ ai.text ~ " of type " ~ A.stringof ~
132 				" cannot be converted to parameter type " ~ MBArgs[ai].stringof);
133 			
134 			import std.conv : emplace;
135 			emplace(&temp[ai], args[ai]);
136 			aarr[ai] = cast(const(void)*)(&temp[ai]);
137 		}
138 	}
139 	static if(!is(Return : void)) RefOrT!Return r = godotDefaultInit!(RefOrT!Return);
140 	
141 	static if(is(Return : void)) alias rptr = Alias!null;
142 	else void* rptr = cast(void*)&r;
143 	
144 	static if(Args.length == 0) alias aptr = Alias!null;
145 	else const(void)** aptr = aarr.ptr;
146 	
147 	_godot_api.godot_method_bind_ptrcall(method.mb, cast(godot_object)self, aptr, rptr);
148 	static if(!is(Return : void)) return r;
149 }
150 
151 /++
152 Variant call, for virtual and vararg methods.
153 
154 Forwards to `callv`, but does compile-time type check of args other than varargs.
155 +/
156 Return callv(MB, Return, Args...)(MB method, godot_object self, Args args)
157 in
158 {
159 	import std.experimental.allocator, std.experimental.allocator.mallocator;
160 	debug if(self.ptr is null)
161 	{
162 		CharString utf8 = (String("Method ")~method.name~String(" called on null reference")).utf8;
163 		auto msg = utf8.data;
164 		assert(0, msg); // leak msg; Error is unrecoverable
165 	}
166 }
167 do
168 {
169 	alias MBArgs = TemplateArgsOf!(MB)[1..$];
170 	
171 	import godot.object;
172 	GodotObject o = void;
173 	o._godot_object = self;
174 	
175 	Array a = Array.make();
176 	static if(Args.length != 0) a.resize(cast(int)Args.length);
177 	foreach(ai, A; Args)
178 	{
179 		static if(is(MBArgs[$-1] : GodotVarArgs) && ai >= MBArgs.length-1)
180 		{
181 			// do nothing
182 		}
183 		else
184 		{
185 			static assert(ai < MBArgs.length, "Too many arguments");
186 			static assert(is(A : MBArgs[ai]) || isImplicitlyConvertible!(A, MBArgs[ai]),
187 				"method" ~ " argument " ~ ai.text ~ " of type " ~ A.stringof ~
188 				" cannot be converted to parameter type " ~ MBArgs[ai].stringof);
189 		}
190 		a[ai] = args[ai];
191 	}
192 	
193 	Variant r = o.callv(method.name, a);
194 	return r.as!Return;
195 }
196 
197 package(godot)
198 mixin template baseCasts()
199 {
200 	private import godot.d.reference, godot.d.traits : RefOrT, NonRef;
201 	
202 	inout(To) as(To)() inout if(isGodotBaseClass!To)
203 	{
204 		static if(extends!(typeof(this), To))
205 			return cast(inout)To(cast()_godot_object);
206 		else static if(extends!(To, typeof(this)))
207 		{
208 			if(_godot_object.ptr is null) return typeof(return).init;
209 			String c = String(To._GODOT_internal_name);
210 			if(isClass(c)) return inout(To)(_godot_object);
211 			return typeof(return).init;
212 		}
213 		else static assert(0, To.stringof ~ " is not polymorphic to "
214 			~ typeof(this).stringof);
215 	}
216 	
217 	inout(To) as(To)() inout if(extendsGodotBaseClass!To)
218 	{
219 		import godot.d.script : NativeScriptTag;
220 		static assert(extends!(To, typeof(this)), "D class " ~ To.stringof
221 			~ " does not extend " ~ typeof(this).stringof);
222 		if(_godot_object.ptr is null) return typeof(return).init;
223 		if(GDNativeVersion.hasNativescript!(1, 1))
224 		{
225 			if(NativeScriptTag!To.matches(_godot_nativescript_api.godot_nativescript_get_type_tag(cast()_godot_object)))
226 			{
227 				return cast(inout(To))(_godot_nativescript_api.godot_nativescript_get_userdata(cast()_godot_object));
228 			}
229 		}
230 		else if(hasMethod(String(`_GDNATIVE_D_typeid`)))
231 		{
232 			return cast(inout(To))(cast(Object)(_godot_nativescript_api.godot_nativescript_get_userdata(cast()_godot_object)));
233 		}
234 		return typeof(return).init;
235 	}
236 	
237 	inout(ToRef) as(ToRef)() inout if(is(ToRef : Ref!To, To) && extends!(To, Reference))
238 	{
239 		import std.traits : TemplateArgsOf, Unqual;
240 		ToRef ret = cast()as!(Unqual!(TemplateArgsOf!ToRef[0]));
241 		return cast(inout)ret;
242 	}
243 	
244 	template opCast(To) if(isGodotBaseClass!To)
245 	{
246 		alias opCast = as!To;
247 	}
248 	template opCast(To) if(extendsGodotBaseClass!To)
249 	{
250 		alias opCast = as!To;
251 	}
252 	template opCast(ToRef) if(is(ToRef : Ref!To, To) && extends!(To, Reference))
253 	{
254 		alias opCast = as!ToRef;
255 	}
256 	// void* cast for passing this type to ptrcalls
257 	package(godot) void* opCast(T : void*)() const { return cast(void*)_godot_object.ptr; }
258 	// strip const, because the C API sometimes expects a non-const godot_object
259 	godot_object opCast(T : godot_object)() const { return cast(godot_object)_godot_object; }
260 	// implicit conversion to bool like D class references
261 	bool opCast(T : bool)() const { return _godot_object.ptr !is null; }
262 }
263 
264 
265