ソースを参照

Merge branch 'zip_iterator'

* zip_iterator:
  Add test coverage, fix bug in >=, add dummy operator->().
  Fill out the rest of the random_access_iterator spec.
  Begin development of zip_iterator.
Sam Jaffe 5 年 前
コミット
350e71454f

+ 110 - 0
include/iterator/zip_iterator.hpp

@@ -0,0 +1,110 @@
+#pragma once
+
+#include <iterator>
+#include <tuple>
+
+#include "detail/arrow_proxy.h"
+
+namespace iterator {
+  template <typename... Iterators> class zip_iterator {
+  public:
+    using value_type = std::tuple<typename Iterators::value_type...>;
+    using reference = std::tuple<typename Iterators::reference...>;
+    using pointer = void; // tuple cannot be accessed by member
+    using difference_type =
+        std::common_type_t<typename Iterators::difference_type...>;
+    using iterator_category =
+        std::common_type_t<typename Iterators::iterator_category...>;
+
+  private:
+    std::tuple<Iterators...> iterators_;
+
+  public:
+    zip_iterator() = default;
+    zip_iterator(Iterators &&... iters) : iterators_(iters...) {}
+
+    reference operator*() const {
+      return reference(*std::get<Iterators>(iterators_)...);
+    }
+
+    pointer operator->() const;
+
+    zip_iterator & operator++() {
+      [[maybe_unused]] auto l = {(++(std::get<Iterators>(iterators_)), 0)...};
+      return *this;
+    }
+
+    zip_iterator operator++(int) {
+      zip_iterator tmp = *this;
+      operator++();
+      return tmp;
+    }
+
+    zip_iterator & operator--() {
+      [[maybe_unused]] auto l = {(--(std::get<Iterators>(iterators_)), 0)...};
+      return *this;
+    }
+
+    zip_iterator operator--(int) {
+      zip_iterator tmp = *this;
+      operator--();
+      return tmp;
+    }
+
+    zip_iterator & operator+=(difference_type d) {
+      [[maybe_unused]] auto l = {
+          ((std::get<Iterators>(iterators_) += d), 0)...};
+      return *this;
+    }
+
+    zip_iterator operator+(difference_type d) {
+      return zip_iterator{*this} += d;
+    }
+
+    reference operator[](difference_type d) { return *(*this + d); }
+
+    zip_iterator & operator-=(difference_type d) {
+      [[maybe_unused]] auto l = {
+          ((std::get<Iterators>(iterators_) -= d), 0)...};
+      return *this;
+    }
+
+    zip_iterator operator-(difference_type d) {
+      return zip_iterator{*this} -= d;
+    }
+
+    difference_type operator-(zip_iterator const & other) {
+      return std::get<0>(iterators_) - std::get<0>(other.iterators_);
+    }
+
+    bool operator==(zip_iterator const & rhs) const {
+      return iterators_ == rhs.iterators_;
+    }
+    bool operator!=(zip_iterator const & rhs) const {
+      return iterators_ != rhs.iterators_;
+    }
+    bool operator<(zip_iterator const & rhs) const {
+      return iterators_ < rhs.iterators_;
+    }
+    bool operator<=(zip_iterator const & rhs) const {
+      return iterators_ <= rhs.iterators_;
+    }
+    bool operator>(zip_iterator const & rhs) const {
+      return iterators_ > rhs.iterators_;
+    }
+    bool operator>=(zip_iterator const & rhs) const {
+      return iterators_ >= rhs.iterators_;
+    }
+  };
+
+  template <typename... Is>
+  zip_iterator<Is...> operator+(typename zip_iterator<Is...>::difference_type d,
+                                zip_iterator<Is...> iter) {
+    return iter += d;
+  }
+}
+
+template <typename... Is>
+iterator::zip_iterator<Is...> make_zip_iterator(Is &&... iters) {
+  return {std::forward<Is>(iters)...};
+}

+ 4 - 0
iterator.xcodeproj/project.pbxproj

@@ -18,6 +18,7 @@
 		CDCB3BDD24E1D5320029B771 /* indexed_iterator_test.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CDCB3BD424E1D5320029B771 /* indexed_iterator_test.cxx */; };
 		CDCB3BDE24E1D5320029B771 /* recursive_iterator_map_test.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CDCB3BD524E1D5320029B771 /* recursive_iterator_map_test.cxx */; };
 		CDCB3BFC24E327CF0029B771 /* recursive_iterator_single_level_test.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CDCB3BFB24E327CF0029B771 /* recursive_iterator_single_level_test.cxx */; };
+		CDCB3C0324E33A1E0029B771 /* zip_iterator_test.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CDCB3C0224E33A1E0029B771 /* zip_iterator_test.cxx */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
@@ -85,6 +86,7 @@
 		CDCB3BD424E1D5320029B771 /* indexed_iterator_test.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = indexed_iterator_test.cxx; sourceTree = "<group>"; };
 		CDCB3BD524E1D5320029B771 /* recursive_iterator_map_test.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = recursive_iterator_map_test.cxx; sourceTree = "<group>"; };
 		CDCB3BFB24E327CF0029B771 /* recursive_iterator_single_level_test.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = recursive_iterator_single_level_test.cxx; sourceTree = "<group>"; };
+		CDCB3C0224E33A1E0029B771 /* zip_iterator_test.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = zip_iterator_test.cxx; sourceTree = "<group>"; };
 		CDEC1E01235167920091D9F2 /* GoogleMock.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = GoogleMock.xcodeproj; path = "../../../gmock-xcode-master/GoogleMock.xcodeproj"; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
@@ -140,6 +142,7 @@
 				CDCB3BD524E1D5320029B771 /* recursive_iterator_map_test.cxx */,
 				CDCB3BCF24E1D5320029B771 /* recursive_iterator_vector_test.cxx */,
 				CDCB3BFB24E327CF0029B771 /* recursive_iterator_single_level_test.cxx */,
+				CDCB3C0224E33A1E0029B771 /* zip_iterator_test.cxx */,
 				CDCB3BCE24E1D5320029B771 /* unkeyed_iterator_test.cxx */,
 			);
 			path = test;
@@ -340,6 +343,7 @@
 				CDCB3BD624E1D5320029B771 /* join_iterator_test.cxx in Sources */,
 				CDCB3BD724E1D5320029B771 /* unkeyed_iterator_test.cxx in Sources */,
 				CDCB3BDA24E1D5320029B771 /* recursive_iterator_mixed_container_test.cxx in Sources */,
+				CDCB3C0324E33A1E0029B771 /* zip_iterator_test.cxx in Sources */,
 				CDCB3BD924E1D5320029B771 /* recursive_iterator_accessors_test.cxx in Sources */,
 				CDCB3BFC24E327CF0029B771 /* recursive_iterator_single_level_test.cxx in Sources */,
 			);

+ 119 - 0
test/zip_iterator_test.cxx

@@ -0,0 +1,119 @@
+//
+//  zip_iterator_test.cxx
+//  iterator-test
+//
+//  Created by Sam Jaffe on 8/11/20.
+//  Copyright © 2020 Sam Jaffe. All rights reserved.
+//
+
+#include "iterator/zip_iterator.hpp"
+
+#include <forward_list>
+#include <list>
+#include <vector>
+
+#include <gmock/gmock.h>
+
+TEST(ZipIteratorTest, CategoryIsMostRestrictiveOfTypes) {
+  std::vector<int> rnd{1, 2, 3};
+  std::vector<std::string> ss{"A", "B", "C"};
+  std::list<int> bid{1, 2, 3};
+  std::forward_list<int> fwd{1, 2, 3};
+  {
+    auto zit = make_zip_iterator(rnd.begin(), ss.begin());
+    testing::StaticAssertTypeEq<decltype(zit)::iterator_category,
+                                std::random_access_iterator_tag>();
+  }
+  {
+    auto zit = make_zip_iterator(rnd.begin(), bid.begin());
+    testing::StaticAssertTypeEq<decltype(zit)::iterator_category,
+                                std::bidirectional_iterator_tag>();
+  }
+  {
+    auto zit = make_zip_iterator(rnd.begin(), fwd.begin());
+    testing::StaticAssertTypeEq<decltype(zit)::iterator_category,
+                                std::forward_iterator_tag>();
+  }
+  {
+    auto zit = make_zip_iterator(bid.begin(), fwd.begin());
+    testing::StaticAssertTypeEq<decltype(zit)::iterator_category,
+                                std::forward_iterator_tag>();
+  }
+}
+
+TEST(ZipIteratorTest, CannotInvokeOperatorArrow) {
+  std::vector<int> is{1, 2, 3};
+  std::vector<std::string> ss{"A", "B", "C"};
+  auto zit = make_zip_iterator(is.begin(), ss.begin());
+  testing::StaticAssertTypeEq<decltype(zit.operator->()), void>();
+}
+
+TEST(ZipIteratorTest, CanCombineParallelObjects) {
+  std::vector<int> is{1, 2, 3};
+  std::vector<std::string> ss{"A", "B", "C"};
+  auto zit = make_zip_iterator(is.begin(), ss.begin());
+  EXPECT_THAT(std::get<0>(*zit), 1);
+  EXPECT_THAT(std::get<1>(*zit), "A");
+}
+
+TEST(ZipIteratorTest, AdvancingMovesAllElements) {
+  std::vector<int> is{1, 2, 3};
+  std::vector<std::string> ss{"A", "B", "C"};
+  auto zit = make_zip_iterator(is.begin(), ss.begin());
+  zit++;
+  EXPECT_THAT(std::get<0>(*zit), 2);
+  EXPECT_THAT(std::get<1>(*zit), "B");
+}
+
+TEST(ZipIteratorTest, CanDecrement) {
+  std::vector<int> is{1, 2, 3};
+  std::vector<std::string> ss{"A", "B", "C"};
+  auto zit = make_zip_iterator(is.begin(), ss.begin());
+  ++zit;
+  zit--;
+  EXPECT_THAT(std::get<0>(*zit), 1);
+  EXPECT_THAT(std::get<1>(*zit), "A");
+}
+
+TEST(ZipIteratorTest, CanMoveByAnyAmount) {
+  std::vector<int> is{1, 2, 3};
+  std::vector<std::string> ss{"A", "B", "C"};
+  auto zit = make_zip_iterator(is.begin(), ss.begin());
+  zit = 2 + zit;
+  EXPECT_THAT(std::get<0>(*zit), 3);
+  EXPECT_THAT(std::get<1>(*zit), "C");
+  zit = zit - 2;
+  EXPECT_THAT(std::get<0>(*zit), 1);
+  EXPECT_THAT(std::get<1>(*zit), "A");
+}
+
+TEST(ZipIteratorTest, CanMarkDistance) {
+  std::vector<int> is{1, 2, 3};
+  std::vector<std::string> ss{"A", "B", "C"};
+  auto zit = make_zip_iterator(is.begin(), ss.begin());
+  auto zend = make_zip_iterator(is.end(), ss.end());
+  EXPECT_THAT(zend - zit, 3);
+}
+
+TEST(ZipIteratorTest, CanRandomAccess) {
+  std::vector<int> is{1, 2, 3};
+  std::vector<std::string> ss{"A", "B", "C"};
+  auto zit = make_zip_iterator(is.begin(), ss.begin());
+  EXPECT_THAT(std::get<0>(zit[1]), 2);
+  EXPECT_THAT(std::get<1>(zit[1]), "B");
+}
+
+TEST(ZipIteratorTest, CanCompareIterators) {
+  std::vector<int> is{1, 2, 3};
+  std::vector<std::string> ss{"A", "B", "C"};
+  auto const zit = make_zip_iterator(is.begin(), ss.begin());
+  auto const zend = make_zip_iterator(is.end(), ss.end());
+  EXPECT_THAT(zit, testing::Eq(make_zip_iterator(is.begin(), ss.begin())));
+  EXPECT_THAT(zit, testing::Ne(zend));
+  EXPECT_THAT(zit, testing::Lt(zend));
+  EXPECT_THAT(zit, testing::Le(zend));
+  EXPECT_THAT(zit, testing::Le(zit));
+  EXPECT_THAT(zend, testing::Gt(zit));
+  EXPECT_THAT(zend, testing::Ge(zit));
+  EXPECT_THAT(zend, testing::Ge(zend));
+}