| 522 | | } |
|---|
| 523 | | |
|---|
| 524 | | |
|---|
| 525 | | /** |
|---|
| 526 | | * $(D Ignore) represents a ignored element. |
|---|
| 527 | | */ |
|---|
| 528 | | struct Ignore {} |
|---|
| 529 | | immutable Ignore ignore; |
|---|
| 530 | | |
|---|
| 531 | | |
|---|
| 532 | | /** |
|---|
| 533 | | * $(D Tie) binds some variables for tuple unpacking |
|---|
| 534 | | * |
|---|
| 535 | | * This struct is a internal implementation. Please use $(D tie) function. |
|---|
| 536 | | * |
|---|
| 537 | | * Example: |
|---|
| 538 | | * ----- |
|---|
| 539 | | * int n; |
|---|
| 540 | | * double d; |
|---|
| 541 | | * |
|---|
| 542 | | * tie(n, d) = tuple(10, 3.14); |
|---|
| 543 | | * |
|---|
| 544 | | * assert(n == 10); |
|---|
| 545 | | * assert(d == 3.14); |
|---|
| 546 | | * ----- |
|---|
| 547 | | * |
|---|
| 548 | | * $(D Tie) can be nested. 'ignore' is used for ignoring assignment. |
|---|
| 549 | | * |
|---|
| 550 | | * Example: |
|---|
| 551 | | * ----- |
|---|
| 552 | | * ubyte n; |
|---|
| 553 | | * double d; |
|---|
| 554 | | * string s; |
|---|
| 555 | | * |
|---|
| 556 | | * tie(ignore, tie(d, s)) = tuple(true, tuple(1.4142, "Hi!")); |
|---|
| 557 | | * |
|---|
| 558 | | * assert(n == 0); |
|---|
| 559 | | * assert(d == 1.4142); |
|---|
| 560 | | * assert(s == "Hi!"); |
|---|
| 561 | | * ----- |
|---|
| 562 | | * |
|---|
| 563 | | * $(D Tie) supports user-defined type. User-defined type needs to implement $(D opTie) method. |
|---|
| 564 | | * |
|---|
| 565 | | * Example: |
|---|
| 566 | | * ----- |
|---|
| 567 | | * struct S |
|---|
| 568 | | * { |
|---|
| 569 | | * uint n; double d; |
|---|
| 570 | | * |
|---|
| 571 | | * void opTie(U...)(ref Tie!U tie) |
|---|
| 572 | | * { |
|---|
| 573 | | * tie = tuple(n, d); |
|---|
| 574 | | * } |
|---|
| 575 | | * } |
|---|
| 576 | | * |
|---|
| 577 | | * auto s = S(10, 3.14); |
|---|
| 578 | | * uint n; |
|---|
| 579 | | * double d; |
|---|
| 580 | | * |
|---|
| 581 | | * tie(n, d) = s; |
|---|
| 582 | | * |
|---|
| 583 | | * assert(n == 10); |
|---|
| 584 | | * assert(d == 3.14); |
|---|
| 585 | | * ----- |
|---|
| 586 | | */ |
|---|
| 587 | | struct Tie(Types...) if (Types.length > 1) |
|---|
| 588 | | { |
|---|
| 589 | | private: |
|---|
| 590 | | template toAssignable(U) |
|---|
| 591 | | { |
|---|
| 592 | | static if (isPointer!(U) || isTie!(U) || is(U == typeof(ignore))) |
|---|
| 593 | | alias U toAssignable; |
|---|
| 594 | | else |
|---|
| 595 | | alias U* toAssignable; |
|---|
| 596 | | } |
|---|
| 597 | | |
|---|
| 598 | | alias staticMap!(toAssignable, Types) T; |
|---|
| 599 | | |
|---|
| 600 | | T captures_; // references to variables |
|---|
| 601 | | |
|---|
| 602 | | |
|---|
| 603 | | public: |
|---|
| 604 | | /** |
|---|
| 605 | | * Constructs a $(D Tie) with $(D_PARAM variables) argument. |
|---|
| 606 | | * |
|---|
| 607 | | * Params: |
|---|
| 608 | | * variables = a tuple to bind. |
|---|
| 609 | | */ |
|---|
| 610 | | this(ref Types variables) |
|---|
| 611 | | { |
|---|
| 612 | | foreach (i, variable; variables) { |
|---|
| 613 | | static if (is(typeof(variable) == typeof(ignore))) {} // ignore |
|---|
| 614 | | // do nothing |
|---|
| 615 | | else static if (isPointer!(Types[i]) || isTie!(Types[i])) // pointer, nested tie |
|---|
| 616 | | captures_[i] = variables[i]; |
|---|
| 617 | | else // basic types |
|---|
| 618 | | captures_[i] = &variables[i]; |
|---|
| 619 | | } |
|---|
| 620 | | } |
|---|
| 621 | | |
|---|
| 622 | | |
|---|
| 623 | | /** |
|---|
| 624 | | * Assignment from $(D_PARAM rhs). |
|---|
| 625 | | * |
|---|
| 626 | | * Params: |
|---|
| 627 | | * rhs = Tuple, Tie, or User-defined type to assign. |
|---|
| 628 | | * |
|---|
| 629 | | * Returns: |
|---|
| 630 | | * this for chaining. |
|---|
| 631 | | */ |
|---|
| 632 | | Tie!Types opAssign(U)(auto ref U rhs) |
|---|
| 633 | | { |
|---|
| 634 | | static if (is(U X == Tuple!(W), W...)) |
|---|
| 635 | | assignTuple(rhs); |
|---|
| 636 | | else static if (__traits(compiles, rhs.opTie)) |
|---|
| 637 | | rhs.opTie(this); |
|---|
| 638 | | else static if (is(U == Tie) && __traits(isRef, rhs)) // nested |
|---|
| 639 | | this.tupleof = rhs.tupleof; |
|---|
| 640 | | else static if (is(U X == Tie!(W), W...)) // chain |
|---|
| 641 | | assignTie(rhs); |
|---|
| 642 | | else |
|---|
| 643 | | static assert(false, "Unsupported type: " ~ U.stringof); |
|---|
| 644 | | |
|---|
| 645 | | return this; |
|---|
| 646 | | } |
|---|
| 647 | | |
|---|
| 648 | | |
|---|
| 649 | | private: |
|---|
| 650 | | void assignTuple(U...)(ref Tuple!U rhs) |
|---|
| 651 | | { |
|---|
| 652 | | static if (isMatching!U) { |
|---|
| 653 | | foreach (i, capture; captures_) { |
|---|
| 654 | | static if (isPointer!(T[i])) // capture |
|---|
| 655 | | *captures_[i] = rhs.field[i]; |
|---|
| 656 | | else static if (is(T[i] V == Tie!(W), W...)) // nested |
|---|
| 657 | | capture = rhs.field[i]; |
|---|
| 658 | | } |
|---|
| 659 | | } else { |
|---|
| 660 | | static assert(false, "Tuple contents are mismatched"); |
|---|
| 661 | | } |
|---|
| 662 | | } |
|---|
| 663 | | |
|---|
| 664 | | |
|---|
| 665 | | void assignTie(U...)(ref Tie!U rhs) |
|---|
| 666 | | { |
|---|
| 667 | | static if (isMatching!U) { |
|---|
| 668 | | foreach (i, capture; captures_) { |
|---|
| 669 | | static if (isPointer!(T[i])) // capture |
|---|
| 670 | | *captures_[i] = *rhs.captures_[i]; |
|---|
| 671 | | else static if (is(T[i] V == Tie!(W), W...)) // nested |
|---|
| 672 | | capture.assignTie(rhs.captures_[i]); |
|---|
| 673 | | } |
|---|
| 674 | | } else { |
|---|
| 675 | | static assert(false, "Tie contents are mismatched"); |
|---|
| 676 | | } |
|---|
| 677 | | } |
|---|
| 678 | | |
|---|
| 679 | | |
|---|
| 680 | | template isMatching(U...) |
|---|
| 681 | | { |
|---|
| 682 | | static if (T.length == U.length) |
|---|
| 683 | | enum isMatching = verify!(0, U).result; |
|---|
| 684 | | else |
|---|
| 685 | | enum isMatching = false; |
|---|
| 686 | | } |
|---|
| 687 | | |
|---|
| 688 | | |
|---|
| 689 | | /* |
|---|
| 690 | | * Verifies each element. |
|---|
| 691 | | */ |
|---|
| 692 | | template verify(uint I, U...) |
|---|
| 693 | | { |
|---|
| 694 | | static assert(T.length == I + U.length, "Caluculation failure"); |
|---|
| 695 | | |
|---|
| 696 | | static if (U.length == 0) { |
|---|
| 697 | | enum result = true; |
|---|
| 698 | | } else { |
|---|
| 699 | | alias T[I] Lhs; |
|---|
| 700 | | alias U[0] Rhs; |
|---|
| 701 | | |
|---|
| 702 | | static if (is(Lhs == typeof(ignore))) // ignore |
|---|
| 703 | | enum result = true && verify!(I + 1, U[1..$]).result; |
|---|
| 704 | | else static if (isImplicitlyConvertible!(Rhs, Types[I])) // capture |
|---|
| 705 | | enum result = true && verify!(I + 1, U[1..$]).result; |
|---|
| 706 | | else static if (isTie!Lhs && isTuple!Rhs) // nested |
|---|
| 707 | | enum result = true && verify!(I + 1, U[1..$]).result; |
|---|
| 708 | | else |
|---|
| 709 | | enum result = false; |
|---|
| 710 | | } |
|---|
| 711 | | } |
|---|
| 712 | | } |
|---|
| 713 | | |
|---|
| 714 | | |
|---|
| 715 | | /** |
|---|
| 716 | | * Binds $(D_PARAM variables) using $(D Tie). |
|---|
| 717 | | * |
|---|
| 718 | | * Params: |
|---|
| 719 | | * variables = the contents to bind. |
|---|
| 720 | | * |
|---|
| 721 | | * Returns: |
|---|
| 722 | | * a $(D Tie) object binds $(D_PARAM variables). |
|---|
| 723 | | */ |
|---|
| 724 | | Tie!(T) tie(T...)(auto ref T variables) |
|---|
| 725 | | { |
|---|
| 726 | | return typeof(return)(variables); |
|---|
| 727 | | } |
|---|
| 728 | | |
|---|
| 729 | | |
|---|
| 730 | | /** |
|---|
| 731 | | * Detects whether T is a $(D Tie) type. |
|---|
| 732 | | */ |
|---|
| 733 | | private template isTie(T) |
|---|
| 734 | | { |
|---|
| 735 | | enum isTie = __traits(compiles, { void f(X...)(Tie!X x) {}; f(T.init); }); |
|---|
| 736 | | } |
|---|
| 737 | | |
|---|
| 738 | | |
|---|
| 739 | | /** |
|---|
| 740 | | * Detects whether T is a $(D Tuple) type. |
|---|
| 741 | | */ |
|---|
| 742 | | private template isTuple(T) |
|---|
| 743 | | { |
|---|
| 744 | | enum isTuple = __traits(compiles, { void f(X...)(Tuple!X x) {}; f(T.init); }); |
|---|
| 745 | | } |
|---|
| 746 | | |
|---|
| 747 | | |
|---|
| 748 | | unittest |
|---|
| 749 | | { |
|---|
| 750 | | { // capture |
|---|
| 751 | | int n; |
|---|
| 752 | | double d; |
|---|
| 753 | | |
|---|
| 754 | | tie(n, d) = tuple(20, 1.4142); |
|---|
| 755 | | |
|---|
| 756 | | assert(n == 20); |
|---|
| 757 | | assert(d == 1.4142); |
|---|
| 758 | | } |
|---|
| 759 | | { // ignore |
|---|
| 760 | | ulong n = 10; |
|---|
| 761 | | double d = 3.14; |
|---|
| 762 | | |
|---|
| 763 | | tie(n, ignore) = tuple(20, 1.4142); |
|---|
| 764 | | |
|---|
| 765 | | assert(n == 20); |
|---|
| 766 | | assert(d == 3.14); |
|---|
| 767 | | |
|---|
| 768 | | tie(ignore, d) = tuple(20, 1.4142); |
|---|
| 769 | | |
|---|
| 770 | | assert(n == 20); |
|---|
| 771 | | assert(d == 1.4142); |
|---|
| 772 | | } |
|---|
| 773 | | { // nested |
|---|
| 774 | | ubyte n; |
|---|
| 775 | | double d; |
|---|
| 776 | | string s; |
|---|
| 777 | | |
|---|
| 778 | | tie(n, tie(d, s)) = tuple(true, tuple(1.4142, "Hi!")); |
|---|
| 779 | | |
|---|
| 780 | | assert(n == 1); |
|---|
| 781 | | assert(d == 1.4142); |
|---|
| 782 | | assert(s == "Hi!"); |
|---|
| 783 | | } |
|---|
| 784 | | { // chain |
|---|
| 785 | | ulong a, b; |
|---|
| 786 | | real c, d; |
|---|
| 787 | | string e, f; |
|---|
| 788 | | |
|---|
| 789 | | tie(a, tie(c, e)) = tie(b, tie(d, f)) = tuple(9, tuple(1.4142L, "Hi!")); |
|---|
| 790 | | |
|---|
| 791 | | assert(a == 9); |
|---|
| 792 | | assert(b == 9); |
|---|
| 793 | | assert(c == 1.4142L); |
|---|
| 794 | | assert(d == 1.4142L); |
|---|
| 795 | | assert(e == "Hi!"); |
|---|
| 796 | | assert(f == "Hi!"); |
|---|
| 797 | | } |
|---|
| 798 | | { // user-defined type |
|---|
| 799 | | static class C |
|---|
| 800 | | { |
|---|
| 801 | | int n_; double d_; |
|---|
| 802 | | |
|---|
| 803 | | this(int n, double d) |
|---|
| 804 | | { |
|---|
| 805 | | n_ = n; |
|---|
| 806 | | d_ = d; |
|---|
| 807 | | } |
|---|
| 808 | | |
|---|
| 809 | | void opTie(U...)(ref Tie!U tie) |
|---|
| 810 | | { |
|---|
| 811 | | tie = tuple(n_, d_); |
|---|
| 812 | | } |
|---|
| 813 | | } |
|---|
| 814 | | |
|---|
| 815 | | auto c = new C(10, 3.14); |
|---|
| 816 | | int n; |
|---|
| 817 | | double d; |
|---|
| 818 | | |
|---|
| 819 | | tie(n, d) = c; |
|---|
| 820 | | |
|---|
| 821 | | assert(n == 10); |
|---|
| 822 | | assert(d == 3.14); |
|---|
| 823 | | } |
|---|