1 module autowrap.reflection; 2 3 4 import std.meta: allSatisfy; 5 import std.traits: isArray, Unqual; 6 import std.typecons: Flag, No; 7 8 private alias I(alias T) = T; 9 private enum isString(alias T) = is(typeof(T) == string); 10 enum isModule(alias T) = is(Unqual!(typeof(T)) == Module); 11 12 /** 13 A module to automatically wrap. 14 Usually not needed since a string will do, but is useful when trying to export 15 all functions from a module by using Module("mymodule", Yes.alwaysExport) 16 instead of "mymodule" 17 */ 18 struct Module { 19 import std.typecons: Flag, No; 20 21 string name; 22 Flag!"alwaysExport" alwaysExport = No.alwaysExport; 23 24 string toString() @safe pure const { 25 import std.conv: text; 26 import std.string: capitalize; 27 return text(`Module("`, name, `", `, text(alwaysExport).capitalize, `.alwaysExport)`); 28 } 29 } 30 31 32 template AllFunctions(Modules...) if(allSatisfy!(isString, Modules)) { 33 import std.meta: staticMap; 34 enum module_(string name) = Module(name); 35 alias AllFunctions = staticMap!(Functions, staticMap!(module_, Modules)); 36 } 37 38 template AllFunctions(Modules...) if(allSatisfy!(isModule, Modules)) { 39 import std.meta: staticMap; 40 alias AllFunctions = staticMap!(Functions, Modules); 41 } 42 43 44 template Functions(Module module_) { 45 mixin(`import dmodule = ` ~ module_.name ~ `;`); 46 alias Functions = Functions!(dmodule, module_.alwaysExport); 47 } 48 49 50 template Functions(alias module_, Flag!"alwaysExport" alwaysExport = No.alwaysExport) 51 if(!is(typeof(module_) == string)) 52 { 53 54 import std.meta: Filter, staticMap; 55 56 template Function(string memberName) { 57 static if(__traits(compiles, I!(__traits(getMember, module_, memberName)))) { 58 alias member = I!(__traits(getMember, module_, memberName)); 59 60 static if(isExportFunction!(member, alwaysExport)) 61 alias Function = FunctionSymbol!(memberName, member); 62 else 63 alias Function = void; 64 } else 65 alias Function = void; 66 } 67 68 template notVoid(A...) if(A.length == 1) { 69 alias T = A[0]; 70 enum notVoid = !is(T == void); 71 } 72 73 alias Functions = Filter!(notVoid, staticMap!(Function, __traits(allMembers, module_))); 74 } 75 76 template FunctionSymbol(string N, alias S) { 77 alias name = N; 78 alias symbol = S; 79 } 80 81 template AllAggregates(ModuleNames...) if(allSatisfy!(isString, ModuleNames)) { 82 import std.meta: staticMap; 83 84 enum module_(string name) = Module(name); 85 enum Modules = staticMap!(module_, ModuleNames); 86 87 alias AllAggregates = AllAggregates!(staticMap!(module_, ModuleNames)); 88 } 89 90 template AllAggregates(Modules...) if(allSatisfy!(isModule, Modules)) { 91 92 import std.meta: NoDuplicates, Filter; 93 import std.traits: isCopyable, Unqual; 94 import std.datetime: Date, DateTime; 95 96 // definitions 97 alias aggregates = AggregateDefinitionsInModules!Modules; 98 99 // return and parameter types 100 alias functionTypes = FunctionTypesInModules!Modules; 101 102 alias copyables = Filter!(isCopyable, NoDuplicates!(aggregates, functionTypes)); 103 104 template notAlreadyWrapped(T) { 105 alias Type = Unqual!T; 106 enum notAlreadyWrapped = !is(Type == Date) && !is(Type == DateTime); 107 } 108 109 alias AllAggregates = Filter!(notAlreadyWrapped, copyables); 110 } 111 112 private template AggregateDefinitionsInModules(Modules...) if(allSatisfy!(isModule, Modules)) { 113 import std.meta: staticMap; 114 alias AggregateDefinitionsInModules = staticMap!(AggregateDefinitionsInModule, Modules); 115 } 116 117 private template AggregateDefinitionsInModule(Module module_) { 118 119 mixin(`import dmodule = ` ~ module_.name ~ `;`); 120 import std.meta: Filter, staticMap, NoDuplicates, AliasSeq; 121 122 alias Member(string memberName) = Symbol!(dmodule, memberName); 123 alias members = staticMap!(Member, __traits(allMembers, dmodule)); 124 alias aggregates = Filter!(isUserAggregate, members); 125 alias recursives = staticMap!(RecursiveAggregates, aggregates); 126 alias all = AliasSeq!(aggregates, recursives); 127 alias AggregateDefinitionsInModule = NoDuplicates!all; 128 } 129 130 131 // All return and parameter types of the functions in the given modules 132 private template FunctionTypesInModules(Modules...) if(allSatisfy!(isModule, Modules)) { 133 import std.meta: staticMap; 134 alias FunctionTypesInModules = staticMap!(FunctionTypesInModule, Modules); 135 } 136 137 138 // All return and parameter types of the functions in the given module 139 private template FunctionTypesInModule(Module module_) { 140 141 mixin(`import dmodule = ` ~ module_.name ~ `;`); 142 import autowrap.reflection: isExportFunction; 143 import std.traits: ReturnType, Parameters; 144 import std.meta: Filter, staticMap, AliasSeq, NoDuplicates; 145 146 alias Member(string memberName) = Symbol!(dmodule, memberName); 147 alias members = staticMap!(Member, __traits(allMembers, dmodule)); 148 enum isWantedExportFunction(alias F) = isExportFunction!(F, module_.alwaysExport); 149 alias functions = Filter!(isWantedExportFunction, members); 150 151 // all return types of all functions 152 alias returns = NoDuplicates!(Filter!(isUserAggregate, staticMap!(PrimordialType, staticMap!(ReturnType, functions)))); 153 // recurse on the types in `returns` to also wrap the aggregate types of the members 154 alias recursiveReturns = NoDuplicates!(staticMap!(RecursiveAggregates, returns)); 155 // all of the parameters types of all of the functions 156 alias params = NoDuplicates!(Filter!(isUserAggregate, staticMap!(PrimordialType, staticMap!(Parameters, functions)))); 157 // recurse on the types in `params` to also wrap the aggregate types of the members 158 alias recursiveParams = NoDuplicates!(staticMap!(RecursiveAggregates, returns)); 159 // chain all types 160 alias functionTypes = AliasSeq!(returns, recursiveReturns, params, recursiveParams); 161 162 alias FunctionTypesInModule = NoDuplicates!(Filter!(isUserAggregate, functionTypes)); 163 } 164 165 166 private template RecursiveAggregates(T) { 167 mixin RecursiveAggregateImpl!(T, RecursiveAggregateHelper); 168 alias RecursiveAggregates = RecursiveAggregateImpl; 169 } 170 171 // Only exists because if RecursiveAggregate recurses using itself dmd complains. 172 // So instead, we ping-pong between identical templates. 173 private template RecursiveAggregateHelper(T) { 174 mixin RecursiveAggregateImpl!(T, RecursiveAggregates); 175 alias RecursiveAggregateHelper = RecursiveAggregateImpl; 176 } 177 178 /** 179 Only exists because if RecursiveAggregate recurses using itself dmd complains. 180 Instead there's a canonical implementation and we ping-pong between two 181 templates that mix this in. 182 */ 183 private mixin template RecursiveAggregateImpl(T, alias Other) { 184 import std.meta: staticMap, Filter, AliasSeq, NoDuplicates; 185 import std.traits: isInstanceOf, Unqual; 186 import std.typecons: Typedef, TypedefType; 187 import std.datetime: Date; 188 189 static if(isInstanceOf!(Typedef, T)) { 190 alias RecursiveAggregateImpl = TypedefType!T; 191 } else static if (is(T == Date)) { 192 alias RecursiveAggregateImpl = Date; 193 } else static if(isUserAggregate!T) { 194 alias AggMember(string memberName) = Symbol!(T, memberName); 195 alias members = staticMap!(AggMember, __traits(allMembers, T)); 196 enum isNotMe(U) = !is(Unqual!T == Unqual!U); 197 198 alias types = staticMap!(Type, members); 199 alias primordials = staticMap!(PrimordialType, types); 200 alias userAggregates = Filter!(isUserAggregate, primordials); 201 alias aggregates = NoDuplicates!(Filter!(isNotMe, userAggregates)); 202 203 static if(aggregates.length == 0) 204 alias RecursiveAggregateImpl = T; 205 else 206 alias RecursiveAggregateImpl = AliasSeq!(aggregates, staticMap!(Other, aggregates)); 207 } else 208 alias RecursiveAggregatesImpl = T; 209 } 210 211 212 private template Type(T...) if(T.length == 1) { 213 static if(is(T[0])) 214 alias Type = T[0]; 215 else 216 alias Type = typeof(T[0]); 217 } 218 219 // if a type is a struct or a class 220 package template isUserAggregate(A...) if(A.length == 1) { 221 import std.datetime; 222 import std.traits: Unqual, isInstanceOf; 223 import std.typecons: Tuple; 224 alias T = A[0]; 225 226 enum isUserAggregate = 227 !is(Unqual!T == DateTime) && 228 !isInstanceOf!(Tuple, T) && 229 (is(T == struct) || is(T == class)); 230 } 231 232 @("DateTime is not a user aggregate") 233 @safe pure unittest { 234 import std.datetime: DateTime; 235 static assert(!isUserAggregate!DateTime); 236 } 237 238 @("Tuple is not a user aggregate") 239 @safe pure unittest { 240 import std.typecons: Tuple; 241 static assert(!isUserAggregate!(Tuple!(int, double))); 242 } 243 244 // Given a parent (module, struct, ...) and a memberName, alias the actual member, 245 // or void if not possible 246 package template Symbol(alias parent, string memberName) { 247 static if(__traits(compiles, I!(__traits(getMember, parent, memberName)))) 248 alias Symbol = I!(__traits(getMember, parent, memberName)); 249 else 250 alias Symbol = void; 251 } 252 253 254 // T -> T, T[] -> T, T[][] -> T 255 private template PrimordialType(T) if(isArray!T) { 256 import std.range.primitives: ElementType; 257 static if(isArray!(ElementType!T)) 258 alias PrimordialType = PrimordialType!(ElementType!T); 259 else 260 alias PrimordialType = ElementType!T; 261 } 262 263 264 // T -> T, T[] -> T, T[][] -> T 265 private template PrimordialType(T) if(!isArray!T) { 266 alias PrimordialType = T; 267 } 268 269 270 @("PrimordialType") 271 unittest { 272 static assert(is(PrimordialType!int == int)); 273 static assert(is(PrimordialType!(int[]) == int)); 274 static assert(is(PrimordialType!(int[][]) == int)); 275 static assert(is(PrimordialType!(double[][]) == double)); 276 static assert(is(PrimordialType!(string[][]) == dchar)); 277 } 278 279 280 package template isExportFunction(alias F, Flag!"alwaysExport" alwaysExport = No.alwaysExport) { 281 import std.traits: isFunction; 282 283 version(AutowrapAlwaysExport) { 284 enum linkage = __traits(getLinkage, F); 285 enum isExportFunction = isFunction!F && linkage != "C" && linkage != "C++"; 286 } else 287 enum isExportFunction = isFunction!F && isExportSymbol!(F, alwaysExport); 288 } 289 290 291 private template isExportSymbol(alias S, Flag!"alwaysExport" alwaysExport = No.alwaysExport) { 292 import std.traits: isFunction; 293 294 version(AutowrapAlwaysExport) 295 enum isExportSymbol = true; 296 else 297 enum isExportSymbol = (alwaysExport || __traits(getProtection, S) == "export"); 298 299 }