1 /** 2 Meta-object that captures all the properties of a function 3 */ 4 5 module bolts.reflection.metafunction; 6 7 import bolts.reflection.metatype; 8 import bolts.reflection.metauda; 9 10 import bolts.meta : AliasPack, Zip; 11 import bolts.traits : isSame; 12 13 import std.array : join; 14 import std.format : format; 15 import std.meta : AliasSeq, aliasSeqOf, ApplyLeft, staticMap, anySatisfy; 16 import std.range : iota; 17 import std.traits; 18 import std.typecons : tuple; 19 20 version(unittest) import fluent.asserts; 21 22 private enum ExtendedFunctionAttribute : ulong 23 { 24 firstExtended = 1UL << 32, 25 static_ = firstExtended << 0, 26 } 27 28 private struct String(string value) 29 { 30 enum mixture = value; 31 enum mixtureArray = [ value ]; 32 } 33 34 template ParameterAttribute(alias F, int i, int j) 35 { 36 static if (is(typeof(F) P == __parameters)) { 37 static if (isExpressions!(__traits(getAttributes, P[i..i+1])[j])) { 38 enum ParameterAttribute = __traits(getAttributes, P[i..i+1])[j]; 39 } else { 40 alias ParameterAttribute = __traits(getAttributes, P[i..i+1])[j]; 41 } 42 } 43 } 44 45 unittest 46 { 47 import bolts.traits : isSame; 48 49 struct foo; 50 51 struct bar 52 { 53 this(string name) { this.name = name; } 54 string name; 55 } 56 57 void f(@foo @bar int, @foo @bar("xyz") @foo @("baz") @(42) real); 58 59 static assert(isSame!(ParameterAttribute!(f, 0, 0), foo)); 60 static assert(isSame!(ParameterAttribute!(f, 0, 1), bar)); 61 static assert(isSame!(ParameterAttribute!(f, 1, 0), foo)); 62 static assert(isSame!(ParameterAttribute!(f, 1, 1), bar("xyz"))); 63 static assert(isSame!(ParameterAttribute!(f, 1, 2), foo)); 64 static assert(isSame!(ParameterAttribute!(f, 1, 3), "baz")); 65 static assert(isSame!(ParameterAttribute!(f, 1, 4), 42)); 66 } 67 68 enum subMixture(alias Meta) = Meta.mixture; 69 enum subMixtureArray(alias Meta) = Meta.mixtureArray; 70 71 private template withDefaults(alias Values, Defaults...) 72 { 73 static if (Values.length != 0) { 74 alias withDefaults = AliasSeq!( 75 Values.Unpack[0], 76 withDefaults!( 77 AliasPack!(Values.Unpack[1..$]), 78 Defaults[1..$])); 79 } else static if (Defaults.length != 0) { 80 alias withDefaults = AliasSeq!( 81 Defaults[0], 82 withDefaults!( 83 Values, 84 Defaults[1..$])); 85 } else { 86 alias withDefaults = AliasSeq!(); 87 } 88 } 89 90 private template aliasSeqReplace(int position, T...) 91 if (T.length > 1) 92 { 93 alias Seq = AliasSeq!(T[1..$]); 94 static if (position == Seq.length) { 95 alias aliasSeqReplace = AliasSeq!(Seq[0..$-1], T[0]); 96 } else static if (position == 0) { 97 alias aliasSeqReplace = AliasSeq!(T[0], Seq[1..$]); 98 } else { 99 alias aliasSeqReplace = AliasSeq!(Seq[0..position], T[0], Seq[position+1..$]); 100 } 101 } 102 103 unittest 104 { 105 static assert( 106 is(aliasSeqReplace!(0, int, char, real, void) 107 == AliasSeq!(int, real, void))); 108 static assert( 109 is(aliasSeqReplace!(1, int, char, real, void) 110 == AliasSeq!(char, int, void))); 111 static assert( 112 is(aliasSeqReplace!(2, int, char, real, void) 113 == AliasSeq!(char, real, int))); 114 115 static assert( 116 is(aliasSeqReplace!(0, int, char, real) 117 == AliasSeq!(int, real))); 118 static assert( 119 is(aliasSeqReplace!(1, int, char, real) 120 == AliasSeq!(char, int))); 121 122 static assert(is(aliasSeqReplace!(0, int, char) == AliasSeq!(int))); 123 } 124 125 alias PDV = ParameterDefaults; 126 127 private enum Property { 128 origin, 129 attributes, 130 returnType, 131 name, 132 index, 133 parameterList, 134 udaList, 135 body_, 136 } 137 138 private alias PropertyDefaults = AliasSeq!( 139 void, 140 0, 141 Type!void, 142 "", 143 0, 144 ParameterList!(), 145 UDAList!(), 146 [], 147 ); 148 149 /** 150 Meta-class Function 151 152 */ 153 154 template Function(Props...) 155 { 156 alias Properties = withDefaults!(AliasPack!(Props), PropertyDefaults); 157 158 import std.ascii : toUpper; 159 160 static foreach (i, property; __traits(allMembers, Property)) { 161 static if (isExpressions!(PropertyDefaults[i])) { 162 mixin("enum ", property, " = Properties[Property.", property, "];"); 163 } else { 164 mixin("alias ", property, " = Properties[Property.", property, "];"); 165 } 166 mixin(q{ 167 alias set%s%s(NewValue...) = Function!( 168 aliasSeqReplace!(Property.%s, NewValue, Properties)); 169 }.format(property[0].toUpper, property[1..$], property)); 170 } 171 172 /** 173 setBody 174 */ 175 176 alias setBody(string newBody) = setBody_!([ "{", newBody, "}" ]); 177 178 179 // -------------------------------------------------------------------------- 180 // function attributes 181 182 alias FA = FunctionAttribute; 183 alias XA = ExtendedFunctionAttribute; 184 185 enum attributeMixtureArray = [] 186 ~ (attributes & XA.static_ ? ["static"] : []) 187 ~ (attributes & FA.pure_ ? ["pure"] : []) 188 ~ (attributes & FA.nothrow_ ? ["nothrow"] : []) 189 ~ (attributes & FA.property ? ["@property"] : []) 190 ~ (attributes & FA.trusted ? ["@trusted"] : []) 191 ~ (attributes & FA.safe ? ["@safe"] : []) 192 ~ (attributes & FA.nogc ? ["@nogc"] : []) 193 ~ (attributes & FA.system ? ["@system"] : []) 194 ~ (attributes & FA.const_ ? ["const"] : []) 195 ~ (attributes & FA.immutable_ ? ["immutable"] : []) 196 ~ (attributes & FA.inout_ ? ["inout"] : []) 197 ~ (attributes & FA.shared_ ? ["shared"] : []) 198 ~ (attributes & FA.return_ ? ["return"] : []) 199 ~ (attributes & FA.scope_ ? ["scope"] : []) 200 ~ (attributes & FA.ref_ ? ["ref"] : []) 201 ; 202 203 enum attributeMixture = attributeMixtureArray.join(" "); 204 205 static foreach ( 206 acc; 207 AliasSeq!( 208 tuple("Static", XA.static_), 209 tuple("Const", FA.const_), 210 tuple("Immutable", FA.immutable_), 211 tuple("Pure", FA.pure_), 212 tuple("Nothrow", FA.nothrow_), 213 tuple("Trusted", FA.trusted), 214 tuple("Safe", FA.safe), 215 tuple("Nogc", FA.nogc), 216 tuple("System", FA.system), 217 tuple("Ref", FA.ref_))) { 218 mixin(q{ 219 enum is%s = (attributes & acc[1]) != 0; 220 alias set%s(bool Set) = void; 221 alias set%s(bool Set : true) = Function!( 222 aliasSeqReplace!(Property.attributes, attributes | acc[1], Properties)); 223 alias set%s(bool Set : false) = Function!( 224 aliasSeqReplace!(Property.attributes, attributes & ~acc[1], Properties)); 225 }.format(acc[0], acc[0], acc[0], acc[0])); 226 } 227 228 enum mutabilityAttributeArray = cast(string[]) [] 229 ~ (isConst ? [ "const" ] : []) 230 ~ (isImmutable ? [ "immutable" ] : []); 231 232 enum mutabilityAttribute = mutabilityAttributeArray.join(""); 233 234 // -------------------------------------------------------------------------- 235 // UDAs 236 237 alias udas = udaList.Unpack; 238 239 enum udaMixtureArray = cast(string[]) [ staticMap!(subMixture, udas) ]; 240 241 enum udaMixture = udaMixtureArray.join(" "); 242 243 enum isSameUDA(alias UDA, alias Meta) = isSame!(UDA, Meta.origin); 244 245 enum hasUDA(UDA...) = anySatisfy!(ApplyLeft!(isSameUDA, UDA[0]), udas); 246 247 // -------------------------------------------------------------------------- 248 // parameters 249 250 alias parameters = parameterList.Unpack; 251 252 enum arity = parameters.length; 253 254 alias setParameters(NewParameters...) = Function!( 255 aliasSeqReplace!(Property.parameterList, ParameterList!(NewParameters), Properties)); 256 257 alias insertParameter(int Position, NewParameter...) = setParameters!( 258 parameters[0..Position], NewParameter, parameters[Position..$]); 259 260 alias appendParameter(NewParameter...) = insertParameter!(arity, NewParameter); 261 262 enum argumentMixtureAt(int i) = parameters[i].argumentMixture; 263 264 enum argumentMixtureArray = cast(string[]) [ 265 staticMap!( 266 argumentMixtureAt, aliasSeqOf!(parameters.length.iota)) 267 ]; 268 269 enum argumentMixture = argumentMixtureArray.join(", "); 270 271 enum parameterMixtureArray = 272 cast(string[]) [staticMap!(subMixture, parameters) ]; 273 274 enum parameterMixture = parameterMixtureArray.join(", "); 275 276 enum declarationMixtureArray = udaMixtureArray ~ attributeMixtureArray 277 ~ returnType.mixtureArray ~ [ "%s(%s)".format(name, parameterMixture) ]; 278 279 enum declarationMixture = declarationMixtureArray.join(" "); 280 281 enum mixtureArray = declarationMixtureArray ~ body_; 282 283 enum mixture = mixtureArray.join(" "); 284 285 enum pointerMixture = 286 Function!(Properties).setName!("function").declarationMixture; 287 } 288 289 /// Examining a function 290 unittest 291 { 292 struct small; 293 pure int add(@small int a, @small int b) { return a + b; } 294 static assert(Function!add.isPure); 295 static assert(Function!add.isNothrow); 296 static assert(Function!add.isNogc); 297 static assert(Function!add.parameters.length == 2); 298 static assert(is(Function!add.parameters[0].type.origin == int)); 299 static assert(Function!add.parameters[0].UDAs.length == 1); 300 static assert(is(Function!add.parameters[0].UDAs[0].origin == small)); 301 } 302 303 /// Generating a function 304 unittest 305 { 306 pure int f() { return 42; } 307 308 template plus1(alias F) 309 { 310 alias NewFunction = Function!(f, "F").setName!"plus1".setBody!("return F() + 1;"); 311 static assert( 312 NewFunction.mixture == 313 "pure nothrow @safe @nogc std.traits.ReturnType!(F) plus1() { return F() + 1; }"); 314 mixin(NewFunction.mixture); 315 } 316 Assert.equal(plus1!f, 43); 317 } 318 319 template makeUDAs(string Source, int Position, UDAs...) 320 { 321 static if (UDAs.length == 0) { 322 alias makeUDAs = AliasSeq!(); 323 } else { 324 alias makeUDAs = AliasSeq!( 325 UDA!( 326 UDAs[0], 327 "__traits(getAttributes, %s)[%d]".format(Source, Position) 328 ), 329 makeUDAs!(Source, Position + 1, UDAs[1..$])); 330 } 331 } 332 333 alias Function(alias Fun, string Source = __traits(identifier, Fun), int Index = 0) = 334 Function!( 335 Fun, 336 functionAttributes!Fun | 337 (__traits(isStaticFunction, Fun) ? ExtendedFunctionAttribute.static_ : 0), 338 Type!(ReturnType!Fun, "std.traits.ReturnType!(%s)".format(Source)), 339 __traits(identifier, Fun), 340 Index, 341 reflectParameterList!(Fun, Source), 342 UDAList!(makeUDAs!(Source, 0, __traits(getAttributes, Fun)))); 343 344 /// Create a Function meta-object from function Fun, 345 alias Function(alias Fun, string Aggregate, string Member, int Index) = 346 Function!( 347 Fun, 348 `__traits(getOverloads, %s, "%s")[%d]`.format(Aggregate, Member, Index), 349 Index); 350 351 unittest 352 { 353 void foo(); 354 Assert.equal( 355 Function!foo.mixture, 356 "@system std.traits.ReturnType!(foo) foo()"); 357 Assert.equal( 358 Function!foo.pointerMixture, 359 "@system std.traits.ReturnType!(foo) function()"); 360 } 361 362 // returnType, setReturnType 363 unittest 364 { 365 void foo(); 366 Assert.equal( 367 Function!foo.returnType.mixture, 368 "std.traits.ReturnType!(foo)"); 369 Assert.equal( 370 Function!foo.setReturnType!(Type!int).returnType.mixture, 371 "int"); 372 Assert.equal( 373 __traits(compiles, Function!foo.setReturnType!int), 374 false); 375 } 376 377 unittest 378 { 379 @safe ref int foo(lazy int i, char c) 380 { 381 static int x; 382 return x; 383 } 384 385 alias Foo = Function!(foo); 386 Assert.equal(Foo.parameters[0].typeMixture, "std.traits.Parameters!(foo)[0]"); 387 Assert.equal(Foo.attributeMixture, "nothrow @safe @nogc ref"); 388 Assert.equal( 389 Foo.mixture, 390 "nothrow @safe @nogc ref std.traits.ReturnType!(foo) foo(lazy std.traits.Parameters!(foo)[0] a0, std.traits.Parameters!(foo)[1] a1)"); 391 392 Assert.equal( 393 (Foo.setName!("bar")).mixture, 394 "nothrow @safe @nogc ref std.traits.ReturnType!(foo) bar(lazy std.traits.Parameters!(foo)[0] a0, std.traits.Parameters!(foo)[1] a1)"); 395 396 static assert(Foo.isSafe); 397 static assert(Foo.isRef); 398 static assert(!Foo.isPure); 399 Assert.equal((Foo.setSafe!false).attributeMixture, "nothrow @nogc ref"); 400 Assert.equal((Foo.setPure!true).attributeMixture, "pure nothrow @safe @nogc ref"); 401 402 struct S 403 { 404 const void foo() {} 405 immutable void bar() {} 406 static @nogc pure @safe void baz(); 407 } 408 409 static assert(Function!(S.foo, "foo").isConst); 410 static assert(Function!(S.bar, "foo").isImmutable); 411 Assert.equal( 412 Function!(S.foo, "foo").mixture, 413 "@system const std.traits.ReturnType!(foo) foo()"); 414 static assert(Function!(S.baz, "baz").isStatic); 415 static assert(Function!(S.baz, "baz").isNogc); 416 static assert(Function!(S.baz, "baz").isPure); 417 static assert(Function!(S.baz, "baz").isSafe); 418 Assert.equal( 419 Function!(S.baz, "baz").mixture, 420 "static pure @safe @nogc std.traits.ReturnType!(baz) baz()"); 421 } 422 423 unittest 424 { 425 void foo(); 426 alias F1 = Function!foo.setParameters!(Parameter!([], Type!int, "j")); 427 Assert.equal( 428 F1.mixture, 429 "@system std.traits.ReturnType!(foo) foo(int j)"); 430 alias F2 = F1.insertParameter!(0, Parameter!([], Type!int, "i")); 431 Assert.equal( 432 F2.mixture, 433 "@system std.traits.ReturnType!(foo) foo(int i, int j)"); 434 alias F3 = F2.appendParameter!(Parameter!([], Type!int, "k")); 435 Assert.equal( 436 "@system std.traits.ReturnType!(foo) foo(int i, int j, int k)", 437 F3.mixture); 438 } 439 440 // ============================================================================ 441 // Parameter 442 443 mixin template mixtureFromArray() 444 { 445 enum mixture = mixtureArray.join(" "); 446 } 447 448 template ParameterDefaultValue(alias Value, string Mixture) 449 { 450 alias origin = Value; 451 452 static if (is(Value == void)) { 453 enum mixtureArray = cast(string[]) []; 454 } else { 455 enum mixtureArray = [ "=", Mixture ]; 456 } 457 458 mixin mixtureFromArray; 459 } 460 461 template Parameter( 462 alias UDAList, string[] StorageClasses, alias Type_, string Name = "", 463 alias DefaultValue = ParameterDefaultValue!(void, "")) 464 { 465 alias udaList = UDAList; 466 alias UDAs = udaList.Unpack; 467 enum storageClasses = StorageClasses; 468 enum name = Name; 469 alias type = Type_; 470 alias defaultValue = DefaultValue; 471 alias setType(alias newType) = Parameter!( 472 udaList, StorageClasses, newType, Name, defaultValue); 473 474 enum storageClassArray = StorageClasses; 475 enum storageClassMixture = storageClassArray.join(" "); 476 477 enum typeMixture = type.mixture; 478 enum typeMixtureArray = type.mixtureArray; 479 import std.traits : Select; 480 enum argumentMixtureArray = Select!(name == "", [], [ name ]); 481 enum argumentMixture = Name; 482 483 enum mixture = join( 484 udaList.mixtureArray 485 ~ storageClassArray 486 ~ typeMixtureArray 487 ~ argumentMixtureArray 488 ~ defaultValue.mixtureArray 489 , " "); 490 491 enum mixtureArray = [ mixture ]; 492 493 static foreach ( 494 sc; [ 495 "In", "Out", "Ref", "Lazy", "Const", "Immutable", "Shared", "Inout", 496 "Scope" ]) { 497 import std.algorithm: canFind; 498 import std.string : toLower; 499 mixin(q{enum is%s = StorageClasses.canFind("%s"); }.format(sc, sc.toLower)); 500 } 501 502 alias setStorageClasses(string[] newStorageClasses) = 503 Parameter!(udaList, newStorageClasses, type, name, defaultValue); 504 } 505 506 alias Parameter(alias Type, string Name = "") = Parameter!([], Type, Name); 507 508 alias Parameter(string[] StorageClasses, alias Type, string Name = "") = 509 Parameter!(UDAList!(), StorageClasses, Type, Name); 510 511 template ParameterList(T...) 512 { 513 alias Unpack = T; 514 enum mixture = mixtureArray.join(" "); 515 enum mixtureArray = cast(string[]) [ staticMap!(subMixture, T) ]; 516 } 517 518 template UDAList(T...) 519 { 520 alias Unpack = T; 521 enum mixtureArray = cast(string[]) [ staticMap!(subMixture, T) ]; 522 enum mixture = mixtureArray.join(" "); 523 } 524 525 template reflectParameterList(alias fun, string mixture) 526 { 527 // disabled because of bug in ParameterDefaults (20744) 528 // static if (is(typeof(ParameterDefaults!fun))) { 529 // alias defaultValues = ParameterDefaults!fun; 530 // } else { 531 // alias Void(int _) = void; 532 // alias defaultValues = staticMap!(Void, aliasSeqOf!(Parameters!fun.length.iota)); 533 // } 534 535 static if (is(typeof(fun) parameters == __parameters)) { 536 alias reflectParameterList = ParameterList!(reflectParameter!0); 537 538 template reflectParameter(int parameterIndex) 539 { 540 static if (parameterIndex == parameters.length) { 541 alias reflectParameter = AliasSeq!(); 542 } else { 543 alias parameter = parameters[parameterIndex .. parameterIndex + 1]; 544 545 alias reflectParameter = AliasSeq!( 546 Parameter!( 547 UDAList!(reflectUDAs!0), 548 [ __traits(getParameterStorageClasses, fun, parameterIndex) ], 549 Type!( 550 parameters[parameterIndex], 551 "std.traits.Parameters!(%s)[%d]".format(mixture, parameterIndex)), 552 "a%d".format(parameterIndex), 553 // ParameterDefaultValue!( 554 // defaultValues[parameterIndex], 555 // "bolts.reflection.metafunction.PDV!(%s)[%d]".format( 556 // mixture, parameterIndex)) 557 ), 558 reflectParameter!(parameterIndex + 1)); 559 560 template reflectUDAs(int udaIndex) 561 { 562 static if (__traits(compiles, __traits(getAttributes, parameter))) { 563 static if (udaIndex < __traits(getAttributes, parameter).length) { 564 static if (isExpressions!(__traits(getAttributes, parameter)[udaIndex])) { 565 enum value = __traits(getAttributes, parameter)[udaIndex]; 566 } else { 567 alias value = __traits(getAttributes, parameter)[udaIndex]; 568 } 569 alias reflectUDAs = AliasSeq!( 570 UDA!( 571 value, 572 "bolts.reflection.metafunction.ParameterAttribute!(%s, %d, %d)".format( 573 mixture, parameterIndex, udaIndex)), 574 reflectUDAs!(udaIndex + 1)); 575 } else { 576 alias reflectUDAs = AliasSeq!(); 577 } 578 } else { 579 alias reflectUDAs = AliasSeq!(); 580 } 581 } 582 } 583 } 584 } 585 } 586 587 unittest 588 { 589 struct attr 590 { 591 this(string name) { this.name = name; } 592 string name; 593 } 594 595 void f(int, @attr int, lazy char, @attr("foo") @(42) out real); 596 597 alias PL = reflectParameterList!(f, "F").Unpack; 598 599 Assert.equal(PL.length, 4); 600 Assert.equal(PL[0].mixture, "std.traits.Parameters!(F)[0] a0"); 601 602 Assert.equal(PL[0].storageClasses.length, 0); 603 Assert.equal(PL[0].UDAs.length, 0); 604 605 Assert.equal(PL[1].storageClasses.length, 0); 606 Assert.equal(PL[1].UDAs.length, 1); 607 Assert.equal(PL[1].UDAs[0].origin.stringof, "attr"); 608 Assert.equal(PL[1].UDAs[0].mixture, "@(bolts.reflection.metafunction.ParameterAttribute!(F, 1, 0))"); 609 610 Assert.equal(PL[2].storageClasses.length, 1); 611 Assert.equal(PL[2].storageClasses[0], "lazy"); 612 Assert.equal(PL[2].UDAs.length, 0); 613 614 Assert.equal(PL[3].storageClasses.length, 1); 615 Assert.equal(PL[3].storageClasses[0], "out"); 616 Assert.equal(PL[3].UDAs.length, 2); 617 Assert.equal(PL[3].UDAs[0].origin.name, "foo"); 618 Assert.equal(PL[3].UDAs[1].origin, 42); 619 620 // Assert.equal( 621 // reflectParameterList!(f, "F").mixture, 622 // ""); 623 } 624 625 unittest 626 { 627 void f(in Object, out Object, ref Object, lazy Object, const Object, immutable Object, 628 // 0 1 2 3 4 5 629 shared Object, inout Object, scope Object); 630 // 6 7 8 631 alias P(int Position) = Parameter!( 632 [__traits(getParameterStorageClasses, f, Position)], 633 Type!Object, 634 ""); 635 //static assert(P!0.isIn); 636 static assert(P!1.isOut); 637 static assert(P!2.isRef); 638 static assert(P!3.isLazy); 639 // for the following the storage class has already become part of the type? 640 //static assert(P!4.isConst); 641 //static assert(P!5.isImmutable); 642 //static assert(P!6.isShared); 643 //static assert(P!7.isInout); 644 static assert(P!8.isScope); 645 646 Assert.equal(P!1.mixture, "out Object"); 647 Assert.equal(P!1.setStorageClasses!([]).mixture, "Object"); 648 } 649 650 unittest 651 { 652 alias P = Parameter!(["lazy"], Type!(int), "a"); 653 Assert.equal(P.typeMixture, "int"); 654 Assert.equal(P.argumentMixture, "a"); 655 Assert.equal(P.storageClassMixture, "lazy"); 656 Assert.equal(P.mixture, "lazy int a"); 657 658 Assert.equal(Parameter!(Type!(int, "T"), "a").typeMixture, "T"); 659 } 660 661 unittest 662 { 663 // building a function from scratch 664 alias F = Function!() 665 .setName!"foo" 666 .setReturnType!(Type!int) 667 .setParameters!( 668 Parameter!(Type!int), 669 Parameter!(Type!string)); 670 Assert.equal(F.mixture, "int foo(int, string)"); 671 } 672 673 // ============================================================================ 674 // UDAs 675 676 unittest 677 { 678 struct foo; 679 @foo @("bar") void f(); 680 alias F = Function!f; 681 Assert.equal(F.udas.length, 2); 682 Assert.equal(F.udas[0].mixture, "@(__traits(getAttributes, f)[0])"); 683 Assert.equal(is(F.udas[0].origin == foo), true); 684 Assert.equal(F.udas[1].origin, "bar"); 685 Assert.equal(F.hasUDA!foo, true); 686 Assert.equal(F.hasUDA!"bar", true); 687 Assert.equal(F.hasUDA!"baz", false); 688 Assert.equal( 689 F.udaMixture, 690 `@(__traits(getAttributes, f)[0]) @(__traits(getAttributes, f)[1])`); 691 mixin(F.setName!"g".mixture, ";"); 692 Assert.equal( 693 __traits(getAttributes, f).stringof, 694 __traits(getAttributes, g).stringof); 695 Assert.equal( 696 Function!f.pointerMixture, 697 `@(__traits(getAttributes, f)[0]) @(__traits(getAttributes, f)[1]) @system std.traits.ReturnType!(f) function()`); 698 } 699 700 unittest 701 { 702 struct foo 703 { 704 this(string name) { this.name = name; } 705 string name; 706 } 707 708 void f(int, @foo @foo("xyz") @("baz") @(42) real); 709 alias F = Function!f; 710 Assert.equal(F.parameters[0].UDAs.length, 0); 711 Assert.equal(F.parameters[1].UDAs.length, 4); 712 } 713 714 // default parameter values 715 // unittest 716 // { 717 // int increment(int i, int by = 1); 718 // Assert.equal(Function!(increment, "F").parameters[1].defaultValue.origin, 1); 719 // Assert.equal( 720 // Function!(increment, "F").parameters[1].mixture, 721 // "std.traits.Parameters!(F)[1] a1 = bolts.reflection.metafunction.PDV!(F)[1]"); 722 // }