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