1 /++ 2 Compile-time introspection of Godot types 3 +/ 4 module godot.d.traits; 5 6 import godotutil..string; 7 import godot.d.udas; 8 import godot.d.reference; 9 10 import std.meta, std.traits; 11 12 import godot.core, godot.c; 13 import godot.object; 14 15 @nogc nothrow: 16 17 template from(string moduleName) 18 { 19 mixin("import from = " ~ moduleName ~ ";"); 20 } 21 22 /++ 23 Adds the Ref wrapper to T, if T is a Reference type 24 +/ 25 template RefOrT(T) 26 { 27 import godot.reference; 28 static if(isGodotClass!T && extends!(T, Reference)) alias RefOrT = Ref!T; 29 else alias RefOrT = T; 30 } 31 32 /++ 33 Removes the Ref wrapper from R, if present 34 +/ 35 template NonRef(R) 36 { 37 static if(is(R : Ref!T, T)) alias NonRef = T; 38 else alias NonRef = R; 39 } 40 41 /++ 42 A UDA with which base Godot classes are marked. NOT used by new D classes. 43 +/ 44 package(godot) enum GodotBaseClass; 45 46 /++ 47 Determine if T is a class originally from the Godot Engine (but *not* a new D 48 class registered to Godot). 49 +/ 50 template isGodotBaseClass(T) 51 { 52 static if(is(T == struct)) enum bool isGodotBaseClass = 53 hasUDA!(T, GodotBaseClass); 54 else enum bool isGodotBaseClass = false; 55 } 56 57 /++ 58 Determine if T is a D native script (extends a Godot base class). 59 +/ 60 template extendsGodotBaseClass(T) 61 { 62 static if(is(T == class) && hasMember!(T, "owner")) 63 { 64 enum bool extendsGodotBaseClass = isGodotBaseClass!(typeof(T.owner)); 65 } 66 else enum bool extendsGodotBaseClass = false; 67 } 68 69 /++ 70 A list of all of T's base classes, both script and C++, ending with GodotObject. 71 72 Has the same purpose as std.traits.BaseClassesTuple, but accounts for Godot's 73 script inheritance system. 74 +/ 75 template GodotBaseClasses(T) 76 { 77 static if(isGodotBaseClass!T) alias GodotBaseClasses = T.BaseClasses; 78 else static if(extendsGodotBaseClass!T) 79 { 80 import std.traits : BaseClassesTuple; 81 // the last two D base classes are GodotScript!<Base> and Object. 82 alias GodotBaseClasses = AliasSeq!(BaseClassesTuple!(Unqual!T)[0..$-2], 83 GodotClass!T, GodotClass!T.BaseClasses); 84 } 85 } 86 87 /++ 88 Checks whether R is a subtype of ParentR by Godot's script inheritance system. 89 Both D script and C++ classes are accounted for. 90 If R and ParentR are the same, `extends` is true as well. 91 +/ 92 template extends(R, ParentR) 93 { 94 alias T = NonRef!R; 95 alias Parent = NonRef!ParentR; 96 static if(is(Unqual!T : Unqual!Parent)) enum bool extends = true; 97 else enum bool extends = staticIndexOf!(Unqual!Parent, GodotBaseClasses!T) != -1; 98 } 99 100 /++ 101 Get the Godot class of R (the class of the `owner` for D native scripts) 102 +/ 103 template GodotClass(R) 104 { 105 alias T = NonRef!R; 106 static if(isGodotBaseClass!T) alias GodotClass = T; 107 else static if(extendsGodotBaseClass!T) alias GodotClass = typeof(T.owner); 108 } 109 110 /++ 111 Determine if T is any Godot class (base C++ class or D native script, but NOT 112 a godot.core struct) 113 +/ 114 enum bool isGodotClass(T) = extendsGodotBaseClass!T || isGodotBaseClass!T; 115 116 /++ 117 Get the C++ Godot Object pointer of either a Godot Object OR a D native script. 118 119 Useful for generic code. 120 +/ 121 GodotClass!T getGodotObject(T)(in T t) if(isGodotClass!T) 122 { 123 GodotClass!T ret; 124 ret._godot_object = t.getGDNativeObject; 125 return ret; 126 } 127 GodotClass!(NonRef!R) getGodotObject(R)(auto ref R r) if(is(R : Ref!U, U)) { return r._reference; } 128 129 package(godot) godot_object getGDNativeObject(T)(in T t) if(isGodotClass!T) 130 { 131 static if(isGodotBaseClass!T) return cast(godot_object)t._godot_object; 132 static if(extendsGodotBaseClass!T) 133 { 134 return (t) ? cast(godot_object)t.owner._godot_object : godot_object.init; 135 } 136 } 137 package(godot) godot_object getGDNativeObject(R)(auto ref R r) if(is(R : Ref!U, U)) { return r._reference._godot_object; } 138 139 /++ 140 Alias to default-constructed T, as an expression. 141 142 A few Godot core types can't use D's `init` because they need to call a C++ 143 constructor through GDNative. 144 +/ 145 template godotDefaultInit(T) 146 { 147 static if(is(T : Array)) alias godotDefaultInit = Alias!(Array.make); 148 else static if(is(T : Dictionary)) alias godotDefaultInit = Alias!( 149 Dictionary.make); 150 else alias godotDefaultInit = Alias!(T.init); 151 } 152 153 /++ 154 Get the Godot-compatible default value of a field in T. 155 +/ 156 auto getDefaultValueFromAlias(T, string fieldName)() 157 { 158 alias a = Alias!(mixin("T."~fieldName)); 159 alias P = typeof(a); 160 161 static if(hasUDA!(a, DefaultValue)) 162 { 163 alias defExprSeq = TemplateArgsOf!(getUDAs!(a, DefaultValue)[0]); 164 static if(isCallable!(defExprSeq[0])) return defExprSeq[0](); 165 else return defExprSeq[0]; 166 } 167 else static if( is(typeof( { P p; } )) ) 168 { 169 import std.math : isNaN; 170 static if(isFloatingPoint!P && a.init.isNaN) 171 { 172 // Godot doesn't support NaNs. Initialize properties to 0.0 instead. 173 return P(0.0); 174 } 175 else return a.init; 176 } 177 else 178 { 179 return Variant.init; 180 } 181 } 182 183 184 package(godot) enum string dName(alias a) = __traits(identifier, a); 185 package(godot) template godotName(alias a) 186 { 187 alias udas = getUDAs!(a, Rename); 188 static if(udas.length == 0) 189 { 190 version(GodotNoAutomaticNamingConvention) enum string godotName = __traits(identifier, a); 191 else enum string godotName = __traits(identifier, a).camelToSnake; 192 } 193 else 194 { 195 static assert(udas.length == 1, "Multiple Rename UDAs on "~ 196 fullyQualifiedName!a~"? Why?"); 197 198 static if(is( udas[0] )) static assert(0, "Construct the UDA with a string: @Rename(\"name\")"); 199 else 200 { 201 enum Rename uda = udas[0]; 202 enum string godotName = uda.name; 203 } 204 } 205 } 206