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