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