1 /** 2 Information about types and symbols at compile-time, 3 similar to std.traits. 4 */ 5 module mirror.traits; 6 7 8 /// Usable as a predicate to std.meta.Filter 9 enum isEnum(T) = is(T == enum); 10 11 /// Usable as a predicate to std.meta.Filter 12 enum isStruct(T) = is(T == struct); 13 14 /// Usable as a predicate to std.meta.Filter 15 enum isInterface(T) = is(T == interface); 16 17 /// Usable as a predicate to std.meta.Filter 18 enum isClass(T) = is(T == class); 19 20 /** 21 If a type is a class or an interface. 22 Usable as a predicate to std.meta.Filter 23 */ 24 enum isOOP(T) = is(T == class) || is(T == interface); 25 26 27 template moduleOf(alias T) { 28 import std.traits: moduleName; 29 mixin(`import `, moduleName!T, `;`); 30 mixin(`alias moduleOf = `, moduleName!T, `;`); 31 } 32 33 34 template isPrivate(alias symbol) { 35 // If a module contains an alias to a basic type, e.g. `alias L = long;`, 36 // then __traits(getProtection, member) fails to compile 37 static if(__traits(compiles, __traits(getProtection, symbol))) 38 enum isPrivate = __traits(getProtection, symbol) == "private"; 39 else 40 enum isPrivate = true; // if it doesn't compile, treat it as private 41 } 42 43 44 /** 45 Retrieves the "fundamental type" of a type T. For most types, this 46 will be exactly the same as T itself. For arrays or pointers, it 47 removes as many "layers" of array or pointer indirections to get to 48 the most basic atomic type possible. Examples of inputs and 49 outputs: 50 51 * T -> T 52 * T[] -> T 53 * T[][] -> T 54 * T* -> T 55 56 */ 57 template FundamentalType(T) { 58 59 import std.traits: isArray, isPointer, PointerTarget; 60 import std.range: ElementEncodingType; 61 62 static if(isArray!T) 63 alias removeOneIndirection = ElementEncodingType!T; 64 else static if(isPointer!T) 65 alias removeOneIndirection = PointerTarget!T; 66 67 private enum isArrayOrPointer(U) = isArray!U || isPointer!U; 68 69 static if(isArrayOrPointer!T) { 70 static if(isArrayOrPointer!removeOneIndirection) 71 alias FundamentalType = FundamentalType!removeOneIndirection; 72 else 73 alias FundamentalType = removeOneIndirection; 74 } else 75 alias FundamentalType = T; 76 } 77 78 79 /** 80 Returns an AliasSeq of all field types of `T`, depth-first 81 recursively. 82 */ 83 alias RecursiveFieldTypes(T) = RecursiveFieldTypesImpl!T; 84 85 86 private template RecursiveFieldTypesImpl(T, alreadySeen...) { 87 88 import mirror.traits: isStruct, isClass; 89 import std.meta: staticMap, AliasSeq, NoDuplicates, Filter, 90 templateNot, staticIndexOf; 91 92 enum isStructOrClass(U) = isStruct!(FundamentalType!U) || isClass!(FundamentalType!U); 93 94 static if(isStructOrClass!T) { 95 96 private alias fields = AliasSeq!(T.tupleof); 97 private alias publicFields = Filter!(templateNot!isPrivate, fields); 98 private alias type(alias symbol) = typeof(symbol); 99 private alias types = staticMap!(type, fields); 100 101 private template recurse(U) { 102 103 static if(isStructOrClass!U) { 104 105 // only recurse if the type hasn't been seen yet to 106 // prevent infinite recursion 107 enum shouldRecurse = staticIndexOf!(U, alreadySeen) == -1; 108 109 static if(shouldRecurse) 110 alias recurse = AliasSeq!(U, RecursiveFieldTypesImpl!(FundamentalType!U, NoDuplicates!(T, types, alreadySeen))); 111 else 112 alias recurse = AliasSeq!(); 113 } else 114 alias recurse = U; 115 } 116 117 alias RecursiveFieldTypesImpl = NoDuplicates!(staticMap!(recurse, types)); 118 } else 119 alias RecursiveFieldTypesImpl = T; 120 } 121 122 123 /** 124 An std.meta.AliasSeq of `T` and all its recursive 125 subtypes. 126 */ 127 template RecursiveTypeTree(T...) { 128 import std.meta: staticMap, NoDuplicates; 129 alias RecursiveTypeTree = NoDuplicates!(T, staticMap!(RecursiveFieldTypes, T)); 130 } 131 132 133 /** 134 Whether or not `F` is a property function 135 */ 136 template isProperty(alias F) { 137 import std.traits: functionAttributes, FunctionAttribute; 138 enum isProperty = functionAttributes!F & FunctionAttribute.property; 139 } 140 141 142 /** 143 All member function symbols in T with overloads represented 144 separately. 145 */ 146 template MemberFunctions(T) if(isStruct!T || isClass!T || isInterface!T) 147 { 148 import mirror.meta: functionsByOverload, Protection; 149 import std.meta: Filter, staticMap; 150 151 private enum isPublic(alias F) = F.protection != Protection.private_; 152 private alias symbolOf(alias S) = S.symbol; 153 154 alias MemberFunctions = Filter!(isMemberFunction, 155 staticMap!(symbolOf, 156 Filter!(isPublic, 157 functionsByOverload!(T, PublicMembers!T)))); 158 } 159 160 161 // must be a global template 162 private template isMemberFunction(alias F) { 163 import std.algorithm: startsWith; 164 165 static if(__traits(compiles, __traits(identifier, F))) { 166 enum name = __traits(identifier, F); 167 alias parent = __traits(parent, F); 168 169 static if(isOOP!parent) { 170 private static bool isWantedFunction(string name) { 171 import std.algorithm: among; 172 return 173 !name.among("toString", "toHash", "opCmp", "opEquals", "factory") 174 && !name.startsWith("__") 175 ; 176 } 177 } else { 178 bool isWantedFunction(string name) { return true; } 179 } 180 private bool isOperator(string name) { 181 return name.startsWith("op") && name.length > 2 && name[2] >= 'A'; 182 } 183 184 enum isOp = isOperator(name); 185 enum isMemberFunction = isWantedFunction(name) && !isOperator(name); 186 187 } else 188 enum isMemberFunction = false; 189 } 190 191 192 template PublicMembers(alias A) { 193 import mirror.traits: isPrivate; 194 import std.meta: Filter, staticMap, Alias, AliasSeq; 195 196 package template member(string name) { 197 198 enum identifier = name; 199 200 static if(__traits(compiles, Alias!(__traits(getMember, A, name)))) { 201 202 alias symbol = Alias!(__traits(getMember, A, name)); 203 204 static if(is(symbol)) 205 alias Type = symbol; 206 else static if(is(typeof(symbol))) 207 alias Type = typeof(symbol); 208 else 209 alias Type = void; 210 211 } else 212 alias symbol = void; 213 } 214 215 private alias members = staticMap!(member, __traits(allMembers, A)); 216 217 // In the `member` template above, if it's not possible to get a member from `A`, 218 // then the symbol is an empty AliasSeq. An example of such a situation can be 219 // found in `modules.problems` from the tests directory, where this causes things 220 // to not compile: `version = OopsVersion;`. 221 // So we filter out such members. 222 private enum hasSymbol(alias member) = !is(member == void); 223 private alias goodMembers = Filter!(hasSymbol, members); 224 225 private enum notPrivate(alias member) = !isPrivate!(member.symbol); 226 227 alias PublicMembers = Filter!(notPrivate, goodMembers); 228 } 229 230 231 package template memberIsSomeFunction(alias member) { 232 import std.traits: isSomeFunction; 233 enum memberIsSomeFunction = isSomeFunction!(member.symbol); 234 } 235 236 237 package template memberIsRegularFunction(alias member) { 238 static if(memberIsSomeFunction!member) { 239 import std.algorithm: startsWith; 240 enum memberIsRegularFunction = 241 !member.identifier.startsWith("_sharedStaticCtor") 242 && !member.identifier.startsWith("_staticCtor") 243 ; 244 } else 245 enum memberIsRegularFunction = false; 246 }