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 modules) { 55 import std.algorithm: map; 56 import std.array: join; 57 import std.typecons: Yes, No; // needed for Module.toString in the mixin 58 59 enum modulesList = modules.value.map!(a => a.toString).join(", "); 60 mixin(`alias AllFunctions = AllFunctions!(`, modulesList, `);`); 61 } 62 63 64 template AllFunctions(Modules...) if(allSatisfy!(isString, Modules)) { 65 import std.meta: staticMap; 66 enum module_(string name) = Module(name); 67 alias AllFunctions = staticMap!(Functions, staticMap!(module_, Modules)); 68 } 69 70 template AllFunctions(Modules...) if(allSatisfy!(isModule, Modules)) { 71 import std.meta: staticMap; 72 alias AllFunctions = staticMap!(Functions, Modules); 73 } 74 75 76 template Functions(Module module_) { 77 mixin(`import dmodule = ` ~ module_.name ~ `;`); 78 alias Functions = Functions!(dmodule, module_.alwaysExport); 79 } 80 81 82 template Functions(alias module_, Flag!"alwaysExport" alwaysExport = No.alwaysExport) 83 if(!is(typeof(module_) == string)) 84 { 85 86 import std.meta: Filter, staticMap; 87 88 template Function(string memberName) { 89 static if(__traits(compiles, I!(__traits(getMember, module_, memberName)))) { 90 alias member = I!(__traits(getMember, module_, memberName)); 91 92 static if(isExportFunction!(member, alwaysExport)) { 93 alias Function = FunctionSymbol!(memberName, module_, moduleName!member, member); 94 } else { 95 alias Function = void; 96 } 97 } else { 98 alias Function = void; 99 } 100 } 101 102 template notVoid(A...) if(A.length == 1) { 103 alias T = A[0]; 104 enum notVoid = !is(T == void); 105 } 106 107 alias Functions = Filter!(notVoid, staticMap!(Function, __traits(allMembers, module_))); 108 } 109 110 template FunctionSymbol(string N, alias M, string MN, alias S) { 111 alias name = N; 112 alias module_ = M; 113 alias moduleName = MN; 114 alias symbol = S; 115 } 116 117 template AllAggregates(Modules modules) { 118 import std.algorithm: map; 119 import std.array: join; 120 import std.typecons: Yes, No; // needed for Module.toString in the mixin 121 122 enum modulesList = modules.value.map!(a => a.toString).join(", "); 123 mixin(`alias AllAggregates = AllAggregates!(`, modulesList, `);`); 124 } 125 126 template AllAggregates(ModuleNames...) if(allSatisfy!(isString, ModuleNames)) { 127 import std.meta: staticMap; 128 129 enum module_(string name) = Module(name); 130 enum Modules = staticMap!(module_, ModuleNames); 131 132 alias AllAggregates = AllAggregates!(staticMap!(module_, ModuleNames)); 133 } 134 135 template AllAggregates(Modules...) if(allSatisfy!(isModule, Modules)) { 136 137 import std.meta: NoDuplicates, Filter; 138 import std.traits: isCopyable, Unqual; 139 import std.datetime: Date, DateTime; 140 141 // definitions 142 alias aggregates = AggregateDefinitionsInModules!Modules; 143 144 // return and parameter types 145 alias functionTypes = FunctionTypesInModules!Modules; 146 147 alias copyables = Filter!(isCopyable, NoDuplicates!(aggregates, functionTypes)); 148 149 template notAlreadyWrapped(T) { 150 alias Type = Unqual!T; 151 enum notAlreadyWrapped = !is(Type == Date) && !is(Type == DateTime); 152 } 153 154 alias notWrapped = Filter!(notAlreadyWrapped, copyables); 155 alias public_ = Filter!(isPublicSymbol, notWrapped); 156 157 alias AllAggregates = public_; 158 } 159 160 private template AggregateDefinitionsInModules(Modules...) if(allSatisfy!(isModule, Modules)) { 161 import std.meta: staticMap; 162 alias AggregateDefinitionsInModules = staticMap!(AggregateDefinitionsInModule, Modules); 163 } 164 165 private template AggregateDefinitionsInModule(Module module_) { 166 167 mixin(`import dmodule = ` ~ module_.name ~ `;`); 168 import std.meta: Filter, staticMap, NoDuplicates, AliasSeq; 169 170 alias Member(string memberName) = Symbol!(dmodule, memberName); 171 alias members = staticMap!(Member, __traits(allMembers, dmodule)); 172 alias aggregates = Filter!(isUserAggregate, members); 173 alias recursives = staticMap!(RecursiveAggregates, aggregates); 174 alias all = AliasSeq!(aggregates, recursives); 175 alias AggregateDefinitionsInModule = NoDuplicates!all; 176 } 177 178 179 // All return and parameter types of the functions in the given modules 180 private template FunctionTypesInModules(Modules...) if(allSatisfy!(isModule, Modules)) { 181 import std.meta: staticMap; 182 alias FunctionTypesInModules = staticMap!(FunctionTypesInModule, Modules); 183 } 184 185 186 // All return and parameter types of the functions in the given module 187 private template FunctionTypesInModule(Module module_) { 188 189 mixin(`import dmodule = ` ~ module_.name ~ `;`); 190 import autowrap.reflection: isExportFunction; 191 import std.traits: ReturnType, Parameters; 192 import std.meta: Filter, staticMap, AliasSeq, NoDuplicates; 193 194 alias Member(string memberName) = Symbol!(dmodule, memberName); 195 alias members = staticMap!(Member, __traits(allMembers, dmodule)); 196 template isWantedExportFunction(T...) if(T.length == 1) { 197 import std.traits: isSomeFunction; 198 alias F = T[0]; 199 static if(isSomeFunction!F) 200 enum isWantedExportFunction = isExportFunction!(F, module_.alwaysExport); 201 else 202 enum isWantedExportFunction = false; 203 } 204 alias functions = Filter!(isWantedExportFunction, members); 205 206 // all return types of all functions 207 alias returns = NoDuplicates!(Filter!(isUserAggregate, staticMap!(PrimordialType, staticMap!(ReturnType, functions)))); 208 // recurse on the types in `returns` to also wrap the aggregate types of the members 209 alias recursiveReturns = NoDuplicates!(staticMap!(RecursiveAggregates, returns)); 210 // all of the parameters types of all of the functions 211 alias params = NoDuplicates!(Filter!(isUserAggregate, staticMap!(PrimordialType, staticMap!(Parameters, functions)))); 212 // recurse on the types in `params` to also wrap the aggregate types of the members 213 alias recursiveParams = NoDuplicates!(staticMap!(RecursiveAggregates, returns)); 214 // chain all types 215 alias functionTypes = AliasSeq!(returns, recursiveReturns, params, recursiveParams); 216 217 alias FunctionTypesInModule = NoDuplicates!(Filter!(isUserAggregate, functionTypes)); 218 } 219 220 221 private template RecursiveAggregates(T) { 222 mixin RecursiveAggregateImpl!(T, RecursiveAggregateHelper); 223 alias RecursiveAggregates = RecursiveAggregateImpl; 224 } 225 226 // Only exists because if RecursiveAggregate recurses using itself dmd complains. 227 // So instead, we ping-pong between identical templates. 228 private template RecursiveAggregateHelper(T) { 229 mixin RecursiveAggregateImpl!(T, RecursiveAggregates); 230 alias RecursiveAggregateHelper = RecursiveAggregateImpl; 231 } 232 233 /** 234 Only exists because if RecursiveAggregate recurses using itself dmd complains. 235 Instead there's a canonical implementation and we ping-pong between two 236 templates that mix this in. 237 */ 238 private mixin template RecursiveAggregateImpl(T, alias Other) { 239 import std.meta: staticMap, Filter, AliasSeq, NoDuplicates; 240 import std.traits: isInstanceOf, Unqual; 241 import std.typecons: Typedef, TypedefType; 242 import std.datetime: Date; 243 244 static if(isInstanceOf!(Typedef, T)) { 245 alias RecursiveAggregateImpl = TypedefType!T; 246 } else static if (is(T == Date)) { 247 alias RecursiveAggregateImpl = Date; 248 } else static if(isUserAggregate!T) { 249 alias AggMember(string memberName) = Symbol!(T, memberName); 250 alias members = staticMap!(AggMember, __traits(allMembers, T)); 251 enum isNotMe(U) = !is(Unqual!T == Unqual!U); 252 253 alias types = staticMap!(Type, members); 254 alias primordials = staticMap!(PrimordialType, types); 255 alias userAggregates = Filter!(isUserAggregate, primordials); 256 alias aggregates = NoDuplicates!(Filter!(isNotMe, userAggregates)); 257 258 static if(aggregates.length == 0) 259 alias RecursiveAggregateImpl = T; 260 else 261 alias RecursiveAggregateImpl = AliasSeq!(aggregates, staticMap!(Other, aggregates)); 262 } else 263 alias RecursiveAggregateImpl = T; 264 } 265 266 267 // must be a global template for staticMap 268 private template Type(T...) if(T.length == 1) { 269 import std.traits: isSomeFunction; 270 import std.meta: AliasSeq; 271 272 static if(isSomeFunction!(T[0])) 273 alias Type = AliasSeq!(); 274 else static if(is(T[0])) 275 alias Type = T[0]; 276 else 277 alias Type = typeof(T[0]); 278 } 279 280 // if a type is a struct or a class 281 package template isUserAggregate(A...) if(A.length == 1) { 282 import std.datetime; 283 import std.traits: Unqual, isInstanceOf; 284 import std.typecons: Tuple; 285 alias T = A[0]; 286 287 enum isUserAggregate = 288 !is(Unqual!T == DateTime) && 289 !isInstanceOf!(Tuple, T) && 290 (is(T == struct) || is(T == class)); 291 } 292 293 294 version(TestingAutowrap) { 295 import std.datetime: DateTime; 296 static assert(!isUserAggregate!DateTime); 297 } 298 299 version(TestingAutowrap) { 300 import std.typecons: Tuple; 301 static assert(!isUserAggregate!(Tuple!(int, double))); 302 } 303 304 // Given a parent (module, struct, ...) and a memberName, alias the actual member, 305 // or void if not possible 306 package template Symbol(alias parent, string memberName) { 307 static if(__traits(compiles, I!(__traits(getMember, parent, memberName)))) 308 alias Symbol = I!(__traits(getMember, parent, memberName)); 309 else 310 alias Symbol = void; 311 } 312 313 314 // T -> T, T[] -> T, T[][] -> T, T* -> T 315 template PrimordialType(T) if(isArray!T) { 316 317 import std.range.primitives: ElementEncodingType; 318 import std.traits: Unqual; 319 320 static if(isArray!(ElementEncodingType!T)) 321 alias PrimordialType = PrimordialType!(ElementEncodingType!T); 322 else 323 alias PrimordialType = Unqual!(ElementEncodingType!T); 324 } 325 326 327 // T -> T, T[] -> T, T[][] -> T, T* -> T 328 template PrimordialType(T) if(!isArray!T) { 329 330 import std.traits: isPointer, PointerTarget, Unqual; 331 332 static if(isPointer!T) { 333 static if(isPointer!(PointerTarget!T)) 334 alias PrimordialType = PrimordialType!(PointerTarget!T); 335 else 336 alias PrimordialType = Unqual!(PointerTarget!T); 337 } else 338 alias PrimordialType = Unqual!T; 339 } 340 341 342 version(TestingAutowrap) { 343 static assert(is(PrimordialType!int == int)); 344 static assert(is(PrimordialType!(int[]) == int)); 345 static assert(is(PrimordialType!(int[][]) == int)); 346 static assert(is(PrimordialType!(double[][]) == double)); 347 static assert(is(PrimordialType!(string[][]) == char)); 348 static assert(is(PrimordialType!(int*) == int)); 349 static assert(is(PrimordialType!(int**) == int)); 350 } 351 352 353 package template isExportFunction(alias F, Flag!"alwaysExport" alwaysExport = No.alwaysExport) { 354 import std.traits: isFunction; 355 356 version(AutowrapAlwaysExport) { 357 static if(isFunction!F) { 358 enum linkage = __traits(getLinkage, F); 359 enum isExportFunction = linkage != "C" && linkage != "C++"; 360 } else 361 enum isExportFunction = false; 362 } else version(AutowrapAlwaysExportC) { 363 static if(isFunction!F) { 364 enum linkage = __traits(getLinkage, F); 365 enum isExportFunction = linkage == "C" || linkage == "C++"; 366 } else 367 enum isExportFunction = false; 368 369 } else { 370 enum isExportFunction = isFunction!F && isExportSymbol!(F, alwaysExport); 371 } 372 } 373 374 375 private template isExportSymbol(alias S, Flag!"alwaysExport" alwaysExport = No.alwaysExport) { 376 version(AutowrapAlwaysExport) 377 enum isExportSymbol = isPublicSymbol!S; 378 else 379 enum isExportSymbol = isPublicSymbol!S && (alwaysExport || __traits(getProtection, S) == "export"); 380 } 381 382 private template isPublicSymbol(alias S) { 383 enum isPublicSymbol = __traits(getProtection, S) == "export" || __traits(getProtection, S) == "public"; 384 } 385 386 387 template PublicFieldNames(T) { 388 import std.meta: Filter, AliasSeq; 389 import std.traits: FieldNameTuple; 390 391 enum isPublic(string fieldName) = __traits(getProtection, __traits(getMember, T, fieldName)) == "public"; 392 alias publicFields = Filter!(isPublic, FieldNameTuple!T); 393 394 // FIXME - See #54 395 static if(is(T == class)) 396 alias PublicFieldNames = AliasSeq!(); 397 else 398 alias PublicFieldNames = publicFields; 399 } 400 401 402 template PublicFieldTypes(T) { 403 import std.meta: staticMap; 404 405 alias fieldType(string name) = typeof(__traits(getMember, T, name)); 406 407 alias PublicFieldTypes = staticMap!(fieldType, PublicFieldNames!T); 408 } 409 410 411 template Properties(functions...) { 412 import std.meta: Filter; 413 alias Properties = Filter!(isProperty, functions); 414 } 415 416 417 template isProperty(alias F) { 418 import std.traits: functionAttributes, FunctionAttribute; 419 enum isProperty = functionAttributes!F & FunctionAttribute.property; 420 } 421 422 423 template isStatic(alias F) { 424 import std.traits: hasStaticMember; 425 enum isStatic = hasStaticMember!(__traits(parent, F), __traits(identifier, F)); 426 } 427 428 @safe pure unittest { 429 static struct Struct { 430 int foo(); 431 static int bar(); 432 } 433 434 static assert(!isStatic!(Struct.foo)); 435 static assert( isStatic!(Struct.bar)); 436 } 437 438 439 // From a function symbol to an AliasSeq of `Parameter` 440 template FunctionParameters(alias F) { 441 import std.traits: Parameters, ParameterIdentifierTuple, ParameterDefaults; 442 import std.meta: staticMap, aliasSeqOf; 443 import std.range: iota; 444 445 alias parameter(size_t i) = Parameter!( 446 Parameters!F[i], 447 ParameterIdentifierTuple!F[i], 448 ParameterDefaults!F[i] 449 ); 450 451 alias FunctionParameters = staticMap!(parameter, aliasSeqOf!(Parameters!F.length.iota)); 452 } 453 454 455 template Parameter(T, string id, D...) if(D.length == 1) { 456 alias Type = T; 457 enum identifier = id; 458 459 static if(is(D[0] == void)) 460 alias Default = void; 461 else 462 enum Default = D[0]; 463 } 464 465 template isParameter(alias T) { 466 import std.traits: TemplateOf; 467 enum isParameter = __traits(isSame, TemplateOf!T, Parameter); 468 }