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.meta;
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._classBinding))
59 			{
60 				static if(n == "_singleton") C._classBinding._singleton = _godot_api
61 					.godot_global_get_singleton(cast(char*)C._classBinding._singletonName);
62 				else static if(n == "_singletonName"){}
63 				else
64 				{
65 					//enum immutable(char*) cn = C._GODOT_internal_name;
66 					mixin("C._classBinding."~n).bind(C._GODOT_internal_name,
67 						getUDAs!(mixin("C._classBinding."~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 = Mallocator.instance.makeArray!(immutable(char))(utf8.data);
95 		throw Mallocator.instance.make!Error(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 = Mallocator.instance.makeArray!(immutable(char))(utf8.data);
164 		throw Mallocator.instance.make!Error(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.empty_array;
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.meta : RefOrT, NonRef;
201 	
202 	To as(To)() if(isGodotBaseClass!To)
203 	{
204 		static if(extends!(typeof(this), To)) return To(_godot_object);
205 		else static if(extends!(To, typeof(this)))
206 		{
207 			if(_godot_object.ptr is null) return typeof(return).init;
208 			String c = String(To._GODOT_internal_name);
209 			if(isClass(c)) return To(_godot_object);
210 			return typeof(return).init;
211 		}
212 		else static assert(0, To.stringof ~ " is not polymorphic to "
213 			~ typeof(this).stringof);
214 	}
215 	
216 	To as(To)() if(extendsGodotBaseClass!To)
217 	{
218 		import godot.d.script : NativeScriptTag;
219 		static assert(extends!(To, typeof(this)), "D class " ~ To.stringof
220 			~ " does not extend " ~ typeof(this).stringof);
221 		if(_godot_object.ptr is null) return typeof(return).init;
222 		if(GDNativeVersion.hasNativescript!(1, 1))
223 		{
224 			if(NativeScriptTag!To.matches(_godot_nativescript_api.godot_nativescript_get_type_tag(_godot_object)))
225 			{
226 				return cast(To)(_godot_nativescript_api.godot_nativescript_get_userdata(_godot_object));
227 			}
228 		}
229 		else if(hasMethod(String(`_GDNATIVE_D_typeid`)))
230 		{
231 			return cast(To)(cast(Object)(_godot_nativescript_api.godot_nativescript_get_userdata(_godot_object)));
232 		}
233 		return typeof(return).init;
234 	}
235 	
236 	ToRef as(ToRef)() if(is(ToRef : Ref!To, To) && extends!(To, Reference))
237 	{
238 		import std.traits : TemplateArgsOf;
239 		return ToRef(as!(TemplateArgsOf!ToRef[0]));
240 	}
241 	
242 	template opCast(To) if(isGodotBaseClass!To)
243 	{
244 		alias opCast = as!To;
245 	}
246 	template opCast(To) if(extendsGodotBaseClass!To)
247 	{
248 		alias opCast = as!To;
249 	}
250 	template opCast(ToRef) if(is(ToRef : Ref!To, To) && extends!(To, Reference))
251 	{
252 		alias opCast = as!ToRef;
253 	}
254 	// void* cast for passing this type to ptrcalls
255 	package(godot) void* opCast(T : void*)() const { return cast(void*)_godot_object.ptr; }
256 	// strip const, because the C API sometimes expects a non-const godot_object
257 	godot_object opCast(T : godot_object)() const { return cast(godot_object)_godot_object; }
258 	// implicit conversion to bool like D class references
259 	bool opCast(T : bool)() const { return _godot_object.ptr !is null; }
260 }
261 
262 
263