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