llvm.org GIT mirror llvm / 790a5cd
[ADT] Add zip_longest iterators. Like the already existing zip_shortest/zip_first iterators, zip_longest iterates over multiple iterators at once, but has as many iterations as the longest sequence. This means some iterators may reach the end before others do. zip_longest uses llvm::Optional's None value to mark a past-the-end value. zip_longest is not reverse-iteratable because the tuples iterated over would be different for different length sequences (IMHO for the same reason neither zip_shortest nor zip_first should be reverse-iteratable; one can still reverse the ranges individually if that's the expected behavior). In contrast to zip_shortest/zip_first, zip_longest tuples contain rvalues instead of references. This is because llvm::Optional cannot contain reference types and the value-initialized default does not have a memory location a reference could point to. The motivation for these iterators is to use C++ foreach to compare two lists of ordered attributes in D48100 (SemaOverload.cpp and ASTReaderDecl.cpp). Idea by @hfinkel. This re-commits r348301 which was reverted by r348303. The compilation error by gcc 5.4 was resolved using make_tuple in the in the initializer_list. The compileration error by msvc14 was resolved by splitting ZipLongestValueType (which already was a workaround for msvc15) into ZipLongestItemType and ZipLongestTupleType. Differential Revision: https://reviews.llvm.org/D48348 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@348323 91177308-0d34-0410-b5e6-96231b3b80d8 Michael Kruse 1 year, 9 months ago
2 changed file(s) with 163 addition(s) and 1 deletion(s). Raw diff Collapse all Expand all
507507 EarlyIncIteratorT(std::end(std::forward(Range))));
508508 }
509509
510 // forward declarations required by zip_shortest/zip_first
510 // forward declarations required by zip_shortest/zip_first/zip_longest
511511 template
512512 bool all_of(R &&range, UnaryPredicate P);
513 template
514 bool any_of(R &&range, UnaryPredicate P);
513515
514516 template struct index_sequence;
515517
657659 detail::zippy zip_first(T &&t, U &&u,
658660 Args &&... args) {
659661 return detail::zippy(
662 std::forward(t), std::forward(u), std::forward(args)...);
663 }
664
665 namespace detail {
666 template
667 static Iter next_or_end(const Iter &I, const Iter &End) {
668 if (I == End)
669 return End;
670 return std::next(I);
671 }
672
673 template
674 static auto deref_or_none(const Iter &I, const Iter &End)
675 -> llvm::Optional
676 typename std::remove_reference::type>::type> {
677 if (I == End)
678 return None;
679 return *I;
680 }
681
682 template struct ZipLongestItemType {
683 using type =
684 llvm::Optional
685 decltype(*std::declval())>::type>::type>;
686 };
687
688 template struct ZipLongestTupleType {
689 using type = std::tuple::type...>;
690 };
691
692 template
693 class zip_longest_iterator
694 : public iterator_facade_base<
695 zip_longest_iterator,
696 typename std::common_type<
697 std::forward_iterator_tag,
698 typename std::iterator_traits::iterator_category...>::type,
699 typename ZipLongestTupleType::type,
700 typename std::iterator_traits
701 0, std::tuple>::type>::difference_type,
702 typename ZipLongestTupleType::type *,
703 typename ZipLongestTupleType::type> {
704 public:
705 using value_type = typename ZipLongestTupleType::type;
706
707 private:
708 std::tuple iterators;
709 std::tuple end_iterators;
710
711 template
712 bool test(const zip_longest_iterator &other,
713 index_sequence) const {
714 return llvm::any_of(
715 std::initializer_list{std::get(this->iterators) !=
716 std::get(other.iterators)...},
717 identity{});
718 }
719
720 template value_type deref(index_sequence) const {
721 return value_type(
722 deref_or_none(std::get(iterators), std::get(end_iterators))...);
723 }
724
725 template
726 decltype(iterators) tup_inc(index_sequence) const {
727 return std::tuple(
728 next_or_end(std::get(iterators), std::get(end_iterators))...);
729 }
730
731 public:
732 zip_longest_iterator(std::pair... ts)
733 : iterators(std::forward(ts.first)...),
734 end_iterators(std::forward(ts.second)...) {}
735
736 value_type operator*() { return deref(index_sequence_for{}); }
737
738 value_type operator*() const { return deref(index_sequence_for{}); }
739
740 zip_longest_iterator &operator++() {
741 iterators = tup_inc(index_sequence_for{});
742 return *this;
743 }
744
745 bool operator==(const zip_longest_iterator &other) const {
746 return !test(other, index_sequence_for{});
747 }
748 };
749
750 template class zip_longest_range {
751 public:
752 using iterator =
753 zip_longest_iterator()))...>;
754 using iterator_category = typename iterator::iterator_category;
755 using value_type = typename iterator::value_type;
756 using difference_type = typename iterator::difference_type;
757 using pointer = typename iterator::pointer;
758 using reference = typename iterator::reference;
759
760 private:
761 std::tuple ts;
762
763 template iterator begin_impl(index_sequence) const {
764 return iterator(std::make_pair(adl_begin(std::get(ts)),
765 adl_end(std::get(ts)))...);
766 }
767
768 template iterator end_impl(index_sequence) const {
769 return iterator(std::make_pair(adl_end(std::get(ts)),
770 adl_end(std::get(ts)))...);
771 }
772
773 public:
774 zip_longest_range(Args &&... ts_) : ts(std::forward(ts_)...) {}
775
776 iterator begin() const { return begin_impl(index_sequence_for{}); }
777 iterator end() const { return end_impl(index_sequence_for{}); }
778 };
779 } // namespace detail
780
781 /// Iterate over two or more iterators at the same time. Iteration continues
782 /// until all iterators reach the end. The llvm::Optional only contains a value
783 /// if the iterator has not reached the end.
784 template
785 detail::zip_longest_range zip_longest(T &&t, U &&u,
786 Args &&... args) {
787 return detail::zip_longest_range(
660788 std::forward(t), std::forward(u), std::forward(args)...);
661789 }
662790
327327 EXPECT_EQ(iters, 4u);
328328 }
329329
330 TEST(ZipIteratorTest, ZipLongestBasic) {
331 using namespace std;
332 const vector pi{3, 1, 4, 1, 5, 9};
333 const vector e{"2", "7", "1", "8"};
334
335 {
336 // Check left range longer than right.
337 const vector, Optional>> expected{
338 make_tuple(3, StringRef("2")), make_tuple(1, StringRef("7")),
339 make_tuple(4, StringRef("1")), make_tuple(1, StringRef("8")),
340 make_tuple(5, None), make_tuple(9, None)};
341 size_t iters = 0;
342 for (auto tup : zip_longest(pi, e)) {
343 EXPECT_EQ(tup, expected[iters]);
344 iters += 1;
345 }
346 EXPECT_EQ(iters, expected.size());
347 }
348
349 {
350 // Check right range longer than left.
351 const vector, Optional>> expected{
352 make_tuple(StringRef("2"), 3), make_tuple(StringRef("7"), 1),
353 make_tuple(StringRef("1"), 4), make_tuple(StringRef("8"), 1),
354 make_tuple(None, 5), make_tuple(None, 9)};
355 size_t iters = 0;
356 for (auto tup : zip_longest(e, pi)) {
357 EXPECT_EQ(tup, expected[iters]);
358 iters += 1;
359 }
360 EXPECT_EQ(iters, expected.size());
361 }
362 }
363
330364 TEST(ZipIteratorTest, Mutability) {
331365 using namespace std;
332366 const SmallVector pi{3, 1, 4, 1, 5, 9};