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 }