1 /++ 2 Templates for wrapping D classes, properties, methods, and signals to be passed 3 to Godot's C interface. 4 +/ 5 module godot.d.wrap; 6 7 import std.range; 8 import std.meta, std.traits; 9 import std.experimental.allocator, std.experimental.allocator.mallocator; 10 import core.stdc.stdlib : malloc, free; 11 12 import godot.d.udas; 13 import godot.d.traits, godot.d.script; 14 15 import godot.core, godot.c; 16 import godot.node; 17 18 private template staticCount(alias thing, seq...) 19 { 20 template staticCountNum(size_t soFar, seq...) 21 { 22 enum size_t nextPos = staticIndexOf!(thing, seq); 23 static if(nextPos == -1) enum size_t staticCountNum = soFar; 24 else enum size_t staticCountNum = staticCountNum!(soFar+1, seq[nextPos+1..$]); 25 } 26 enum size_t staticCount = staticCountNum!(0, seq); 27 } 28 29 private string overloadError(methods...)() 30 { 31 alias godotNames = staticMap!(godotName, methods); 32 foreach(m; methods) 33 { 34 static if(staticCount!(godotName!m, godotNames) > 1) 35 { 36 static assert(0, `Godot does not support overloading methods (` 37 ~ fullyQualifiedName!m ~ `, wrapped as "` ~ godotName!m ~ 38 `"); rename one with @Rename("new_name") or use Variant args`); 39 } 40 } 41 } 42 43 package(godot) template godotMethods(T) 44 { 45 alias mfs(alias mName) = MemberFunctionsTuple!(T, mName); 46 alias allMfs = staticMap!(mfs, __traits(derivedMembers, T)); 47 enum bool isMethod(alias mf) = hasUDA!(mf, Method); 48 49 alias godotMethods = Filter!(isMethod, allMfs); 50 51 alias godotNames = staticMap!(godotName, godotMethods); 52 static assert(godotNames.length == NoDuplicates!godotNames.length, 53 overloadError!godotMethods()); 54 } 55 56 package(godot) template godotSignals(T) 57 { 58 enum isSignalExpr(string n) = q{ isCallable!(mixin("T."~n)) 59 && ( hasUDA!(mixin("T."~n), Signal) || is(ReturnType!(mixin("T."~n)) == Signal) ) }; 60 template isSignal(string n) 61 { 62 static if( __traits(compiles, mixin(isSignalExpr!n)) ) 63 { 64 enum bool isSignal = mixin(isSignalExpr!n); 65 } 66 else enum bool isSignal = false; 67 } 68 alias godotSignals = Filter!(isSignal, __traits(derivedMembers, T)); 69 } 70 71 package(godot) template onReadyFieldNames(T) 72 { 73 import godot.node; 74 static if(!is(GodotClass!T : Node)) alias onReadyFieldNames = AliasSeq!(); 75 else 76 { 77 alias fieldNames = FieldNameTuple!T; 78 template isORField(string n) 79 { 80 static if(staticIndexOf!(n, fieldNames) != -1 && staticIndexOf!(__traits 81 (getProtection, __traits(getMember, T, n)), "public", "export") != -1) 82 { 83 enum bool isORField = hasUDA!(__traits(getMember, T, n), OnReady); 84 } 85 else enum bool isORField = false; 86 } 87 alias onReadyFieldNames = Filter!(isORField, __traits(derivedMembers, T)); 88 } 89 } 90 91 package(godot) template godotPropertyGetters(T) 92 { 93 alias mfs(alias mName) = MemberFunctionsTuple!(T, mName); 94 alias allMfs = staticMap!(mfs, __traits(derivedMembers, T)); 95 template isGetter(alias mf) 96 { 97 enum bool isGetter = hasUDA!(mf, Property) && !is(ReturnType!mf == void); 98 } 99 100 alias godotPropertyGetters = Filter!(isGetter, allMfs); 101 102 alias godotNames = Filter!(godotName, godotPropertyGetters); 103 static assert(godotNames.length == NoDuplicates!godotNames.length, 104 overloadError!godotPropertyGetters()); 105 } 106 107 package(godot) template godotPropertySetters(T) 108 { 109 alias mfs(alias mName) = MemberFunctionsTuple!(T, mName); 110 alias allMfs = staticMap!(mfs, __traits(derivedMembers, T)); 111 template isSetter(alias mf) 112 { 113 enum bool isSetter = hasUDA!(mf, Property) && is(ReturnType!mf == void); 114 } 115 116 alias godotPropertySetters = Filter!(isSetter, allMfs); 117 118 alias godotNames = Filter!(godotName, godotPropertySetters); 119 static assert(godotNames.length == NoDuplicates!godotNames.length, 120 overloadError!godotPropertySetters()); 121 } 122 123 package(godot) template godotPropertyNames(T) 124 { 125 alias godotPropertyNames = NoDuplicates!(staticMap!(godotName, godotPropertyGetters!T, 126 godotPropertySetters!T)); 127 } 128 129 package(godot) template godotPropertyVariableNames(T) 130 { 131 alias fieldNames = FieldNameTuple!T; 132 alias field(string name) = Alias!(__traits(getMember, T, name)); 133 template isVariable(string name) 134 { 135 static if(__traits(getProtection, __traits(getMember, T, name))=="public") 136 enum bool isVariable = hasUDA!(field!name, Property); 137 else enum bool isVariable = false; 138 } 139 140 alias godotPropertyVariableNames = Filter!(isVariable, fieldNames); 141 } 142 143 /// get the common Variant type for a set of function or variable aliases 144 package(godot) template extractPropertyVariantType(seq...) 145 { 146 template Type(alias a) 147 { 148 static if(isFunction!a && is(ReturnType!a == void)) alias Type = Parameters!a[0]; 149 else static if(isFunction!a) alias Type = NonRef!(ReturnType!a); 150 //else alias Type = typeof(a); 151 152 static assert(Variant.compatible!Type, "Property type "~ 153 Type.stringof~" is incompatible with Variant."); 154 } 155 alias types = NoDuplicates!( staticMap!( Variant.variantTypeOf, staticMap!(Type, seq) ) ); 156 static assert(types.length == 1); /// TODO: better error message 157 enum extractPropertyVariantType = types[0]; 158 } 159 160 package(godot) template extractPropertyUDA(seq...) 161 { 162 template udas(alias a) 163 { 164 alias udas = getUDAs!(a, Property); 165 } 166 enum bool isUDAValue(alias a) = !is(a); 167 alias values = Filter!(isUDAValue, staticMap!(udas, seq)); 168 169 static if(values.length == 0) enum Property extractPropertyUDA = Property.init; 170 else static if(values.length == 1) enum Property extractPropertyUDA = values[0]; 171 else 172 { 173 // verify that they all have the same value, to avoid wierdness 174 enum Property extractPropertyUDA = values[0]; 175 enum bool isSameAsFirst(Property p) = extractPropertyUDA == p; 176 static assert(allSatisfy!(isSameAsFirst, values[1..$])); 177 } 178 } 179 180 /++ 181 Variadic template for method wrappers. 182 183 Params: 184 T = the class that owns the method 185 mf = the member function being wrapped, as an alias 186 +/ 187 package(godot) struct MethodWrapper(T, alias mf) 188 { 189 alias R = ReturnType!mf; // the return type (can be void) 190 alias A = Parameters!mf; // the argument types (can be empty) 191 192 enum string name = __traits(identifier, mf); 193 194 /++ 195 C function passed to Godot that calls the wrapped method 196 +/ 197 extern(C) // for calling convention 198 static godot_variant callMethod(godot_object o, void* methodData, 199 void* userData, int numArgs, godot_variant** args) 200 { 201 // TODO: check types for Variant compatibility, give a better error here 202 // TODO: check numArgs, accounting for D arg defaults 203 204 godot_variant vd; 205 _godot_api.godot_variant_new_nil(&vd); 206 Variant* v = cast(Variant*)&vd; // just a pointer; no destructor will be called 207 208 T obj = cast(T)userData; 209 210 A[ai] variantToArg(size_t ai)() 211 { 212 return (cast(Variant*)args[ai]).as!(A[ai]); 213 } 214 template ArgCall(size_t ai) 215 { 216 alias ArgCall = variantToArg!ai; //A[i] function() 217 } 218 219 alias argIota = aliasSeqOf!(iota(A.length)); 220 alias argCall = staticMap!(ArgCall, argIota); 221 222 static if(is(R == void)) 223 { 224 mixin("obj." ~ name ~ "(argCall);"); 225 } 226 else 227 { 228 mixin("*v = obj." ~ name ~ "(argCall);"); 229 } 230 231 return vd; 232 } 233 234 /++ 235 C function passed to Godot if this is a property getter 236 +/ 237 static if(!is(R == void) && A.length == 0) 238 extern(C) // for calling convention 239 static godot_variant callPropertyGet(godot_object o, void* methodData, 240 void* userData) 241 { 242 godot_variant vd; 243 _godot_api.godot_variant_new_nil(&vd); 244 Variant* v = cast(Variant*)&vd; // just a pointer; no destructor will be called 245 246 T obj = cast(T)userData; 247 248 mixin("*v = obj." ~ name ~ "();"); 249 250 return vd; 251 } 252 253 /++ 254 C function passed to Godot if this is a property setter 255 +/ 256 static if(is(R == void) && A.length == 1) 257 extern(C) // for calling convention 258 static void callPropertySet(godot_object o, void* methodData, 259 void* userData, godot_variant* arg) 260 { 261 Variant* v = cast(Variant*)arg; 262 263 T obj = cast(T)userData; 264 265 auto vt = v.as!(A[0]); 266 mixin("obj." ~ name ~ "(vt);"); 267 } 268 } 269 270 package(godot) struct OnReadyWrapper(T) if(is(GodotClass!T : Node)) 271 { 272 extern(C) // for calling convention 273 static godot_variant callOnReady(godot_object o, void* methodData, 274 void* userData, int numArgs, godot_variant** args) 275 { 276 T t = cast(T)userData; 277 278 foreach(n; onReadyFieldNames!T) 279 { 280 alias udas = getUDAs!(__traits(getMember, T, n), OnReady); 281 static assert(udas.length == 1, "Multiple OnReady UDAs on "~T.stringof~"."~n); 282 283 alias A = Alias!(TemplateArgsOf!(udas[0])[0]); 284 alias F = typeof(mixin("T."~n)); 285 286 // First, determine where to obtain the value to assign, and put it in `result`. 287 // `result` will be alias to void if nothing to assign. 288 static if(isCallable!A) 289 { 290 // pass the class itself to the function 291 static if(Parameters!A.length && isImplicitlyConvertible!(T, Parameters!A[0])) 292 alias arg = t; 293 else alias arg = AliasSeq!(); 294 static if(is(ReturnType!A == void)) 295 { 296 alias result = void; 297 A(arg); 298 } 299 else 300 { 301 auto result = A(arg); /// temp variable for return value 302 } 303 } 304 else static if(is(A)) static assert(0, "OnReady arg can't be a type"); 305 else static if(isExpressions!A) // expression (string literal, etc) 306 { 307 alias result = A; 308 } 309 else // some other alias (a different variable identifier?) 310 { 311 static if(__traits(compiles, __traits(parent, A))) 312 alias P = Alias!(__traits(parent, A)); 313 else alias P = void; 314 static if(is(T : P)) 315 { 316 // A is another variable inside this very same T 317 auto result = __traits(getMember, t, __traits(identifier, A)); 318 } 319 else alias result = A; // final fallback: pass it unmodified to assignment 320 } 321 322 // Second, assign `result` to the field depending on the types of it and `result` 323 static if(!is(result == void)) 324 { 325 import godot.resource; 326 327 static if(isImplicitlyConvertible!(typeof(result), F)) 328 { 329 // direct assignment 330 mixin("t."~n) = result; 331 } 332 else static if(__traits(compiles, mixin("t."~n) = F(result))) 333 { 334 // explicit constructor (String(string), NodePath(string), etc) 335 mixin("t."~n) = F(result); 336 } 337 else static if(isGodotClass!F && extends!(F, Node)) 338 { 339 // special case: node path 340 mixin("t."~n) = cast(F)t.owner.getNode(result); 341 } 342 else static if(isGodotClass!F && extends!(F, Resource)) 343 { 344 // special case: resource load path 345 import godot.resourceloader; 346 mixin("t."~n) = cast(F)ResourceLoader.load(result); 347 } 348 else static assert(0, "Don't know how to assign "~typeof(result).stringof~" "~result.stringof~ 349 " to "~F.stringof~" "~fullyQualifiedName!(mixin("t."~n))); 350 } 351 } 352 353 // Finally, call the actual _ready() if it exists. 354 enum bool isReady(alias func) = "_ready" == godotName!func; 355 alias readies = Filter!(isReady, godotMethods!T); 356 static if(readies.length) mixin("t."~__traits(identifier, readies[0])~"();"); 357 358 godot_variant nil; 359 _godot_api.godot_variant_new_nil(&nil); 360 return nil; 361 } 362 } 363 364 /++ 365 Template for public variables exported as properties. 366 367 Params: 368 T = the class that owns the variable 369 var = the name of the member variable being wrapped 370 +/ 371 package(godot) struct VariableWrapper(T, string var) 372 { 373 import godot.reference, godot.d.reference; 374 alias P = typeof(mixin("T."~var)); 375 static if(extends!(P, Reference)) static assert(is(P : Ref!U, U), 376 "Reference type property "~T.stringof~"."~var~" must be ref-counted as Ref!(" 377 ~P.stringof~")"); 378 379 extern(C) // for calling convention 380 static godot_variant callPropertyGet(godot_object o, void* methodData, 381 void* userData) 382 { 383 T obj = cast(T)userData; 384 385 godot_variant vd; 386 _godot_api.godot_variant_new_nil(&vd); 387 Variant* v = cast(Variant*)&vd; // just a pointer; no destructor will be called 388 389 *v = mixin("obj."~var); 390 391 return vd; 392 } 393 394 extern(C) // for calling convention 395 static void callPropertySet(godot_object o, void* methodData, 396 void* userData, godot_variant* arg) 397 { 398 T obj = cast(T)userData; 399 400 Variant* v = cast(Variant*)arg; 401 402 auto vt = v.as!P; 403 mixin("obj."~var) = vt; 404 } 405 } 406 407 extern(C) package(godot) void emptySetter(godot_object self, void* methodData, 408 void* userData, godot_variant* value) 409 { 410 assert(0, "Can't call empty property setter"); 411 //return; 412 } 413 414 extern(C) package(godot) godot_variant emptyGetter(godot_object self, void* methodData, 415 void* userData) 416 { 417 assert(0, "Can't call empty property getter"); 418 /+godot_variant v; 419 _godot_api.godot_variant_new_nil(&v); 420 return v;+/ 421 } 422