Index: include/llvm/ADT/STLExtras.h =================================================================== --- include/llvm/ADT/STLExtras.h +++ include/llvm/ADT/STLExtras.h @@ -721,6 +721,83 @@ return detail::apply_tuple_impl(std::forward(f), std::forward(t), Indices()); } + +namespace detail { +template struct zipper { +private: + template + static bool any_equal_pair(const std::tuple &LHS, + const std::tuple &RHS) { + if (std::get(LHS) == std::get(RHS)) + return true; + return any_equal_pair(LHS, RHS); + } + template <> + static bool any_equal_pair(const std::tuple &LHS, + const std::tuple &RHS) { + return false; + } + + static auto deref_iter_tuple(Iter... Items) { + return std::tuple(*std::forward(Items)...); + } + + static auto increment_iter_tuple(Iter... Items) { + return std::make_tuple(++std::forward(Items)...); + } + +public: + explicit zipper(std::tuple &&Begins, std::tuple &&Ends) + : Begins(std::move(Begins)), Ends(std::move(Ends)) {} + + struct iterator { + iterator(std::tuple Pos) : Pos(Pos) {} + + auto operator*() { return apply_tuple(deref_iter_tuple, Pos); } + + auto operator*() const { return apply_tuple(deref_iter_tuple, Pos); } + + iterator &operator++() { + Pos = apply_tuple(increment_iter_tuple, Pos); + return *this; + } + + bool operator==(const iterator &RHS) const { + // Return true if *any* of the items are equal. This way consider a + // tuple of iterators where any one item refers to the end of the zipped + // members as referring to the end of the entire zipped sequence. + return zipper::any_equal_pair<0>(Pos, RHS.Pos); + } + + bool operator!=(const iterator &RHS) const { return !(*this == RHS); } + + private: + std::tuple Pos; + }; + + iterator begin() { return iterator(Begins); } + iterator end() { return iterator(Ends); } + + iterator begin() const { return iterator(Begins); } + iterator end() const { return iterator(Ends); } + +private: + std::tuple Begins; + std::tuple Ends; +}; + +template +auto make_zipper(std::tuple &&Begins, std::tuple &&Ends) { + return zipper(std::move(Begins), std::move(Ends)); +} +} + +template auto zip(Rs &&... Ranges) { + return detail::make_zipper( + std::make_tuple(std::begin(std::forward(Ranges))...), + std::make_tuple(std::end(std::forward(Ranges))...)); +} + } // End llvm namespace #endif Index: unittests/ADT/STLExtrasTest.cpp =================================================================== --- unittests/ADT/STLExtrasTest.cpp +++ unittests/ADT/STLExtrasTest.cpp @@ -136,7 +136,7 @@ static_assert( std::is_same, decltype(Values)>::value, - "Incorrect tuple type!"); + "Tuple elements are not value types!"); EXPECT_EQ(1, std::get<0>(Values)); EXPECT_EQ('A', std::get<1>(Values)); @@ -149,4 +149,75 @@ EXPECT_EQ('B', std::get<1>(Values)); EXPECT_EQ("B", std::get<2>(Values)); } + +TEST(STLExtrasTest, Zip) { + std::vector A = {1, 2, 3}; + std::vector B = {'A', 'B', 'C'}; + std::vector C = {"A", "B", "C"}; + + std::vector> Results; + for (auto entries : zip(A, B, C)) { + static_assert(std::is_same, + decltype(entries)>::value, + "tuple entries are not reference types!"); + Results.push_back(entries); + } + + ASSERT_EQ(3u, Results.size()); + EXPECT_EQ(std::make_tuple(1, 'A', "A"), Results[0]); + EXPECT_EQ(std::make_tuple(2, 'B', "B"), Results[1]); + EXPECT_EQ(std::make_tuple(3, 'C', "C"), Results[2]); +} + +TEST(STLExtrasTest, ZipUneven) { + std::vector A = {1, 2, 3, 4, 5}; + std::vector B = {'A', 'B', 'C', 'D'}; + std::vector C = {"A", "B", "C", "D", "E", "F", "G"}; + + std::vector> Results; + for (auto entries : zip(A, B, C)) { + Results.push_back(entries); + } + + ASSERT_EQ(4u, Results.size()); + EXPECT_EQ(std::make_tuple(1, 'A', "A"), Results[0]); + EXPECT_EQ(std::make_tuple(2, 'B', "B"), Results[1]); + EXPECT_EQ(std::make_tuple(3, 'C', "C"), Results[2]); + EXPECT_EQ(std::make_tuple(4, 'D', "D"), Results[3]); +} + +TEST(STLExtrasTest, ZipEmpty) { + std::vector A = {}; + std::vector B = {'A', 'B', 'C', 'D'}; + std::vector C = {"A", "B", "C", "D", "E", "F", "G"}; + + std::vector> Results; + for (auto entries : zip(A, B, C)) { + Results.push_back(entries); + } + + EXPECT_TRUE(Results.empty()); +} + +TEST(STLExtrasTest, ZipModifyNonConst) { + std::vector A = {0, 1, 2}; + std::vector B = {'A', 'B', 'C'}; + const std::vector C = {"A", "B", "C"}; + + for (auto entries : zip(A, B, C)) { + static_assert(std::is_same, + decltype(entries)>::value, + "tuple entries are not reference types!"); + std::get<0>(entries) += 42; + std::get<1>(entries) -= 'A'; + } + + EXPECT_EQ(42, A[0]); + EXPECT_EQ(43, A[1]); + EXPECT_EQ(44, A[2]); + + EXPECT_EQ(0, B[0]); + EXPECT_EQ(1, B[1]); + EXPECT_EQ(2, B[2]); +} }