فهرست منبع

Complete test coverage for end_aware_iterator.
Add more coverage for joining_iterator and indexed_iterator.
Add support for operator-> on indexed iterator, others to come.

Coverage remaining:
- indexed_iterator: 32 LoC
- join_iterator: 9 LoC
- recursive_iterator: ~36 LoC
- unkeyed_iterator: 29 LoC

Sam Jaffe 5 سال پیش
والد
کامیت
0a0b490838

+ 11 - 0
include/iterator/detail/arrow_proxy.h

@@ -0,0 +1,11 @@
+/**
+ * https://quuxplusone.github.io/blog/2019/02/06/arrow-proxy/
+ */
+#pragma once
+
+namespace iterator { namespace detail {
+  template <typename Reference> struct arrow_proxy {
+    Reference r;
+    Reference * operator->() { return std::addressof(r); }
+  };
+}}

+ 7 - 1
include/iterator/indexed_iterator.hpp

@@ -9,6 +9,8 @@
 
 #include <iterator>
 
+#include "detail/arrow_proxy.h"
+
 namespace iterator {
   template <typename Iterator> class indexed_iterator {
   private:
@@ -19,7 +21,7 @@ namespace iterator {
     using index_type = std::size_t;
     using value_type = std::pair<index_type, base_value_type>;
     using reference = std::pair<index_type, base_reference>;
-    using pointer = void;
+    using pointer = detail::arrow_proxy<reference>;
     using difference_type =
         typename std::iterator_traits<Iterator>::difference_type;
     using iterator_category =
@@ -34,6 +36,7 @@ namespace iterator {
         : _base(oiter._base), _index(oiter._index) {}
 
     reference operator*() const { return reference{_index, *_base}; }
+    pointer operator->() const { return {operator*()}; }
 
     indexed_iterator & operator++() {
       ++_base;
@@ -73,6 +76,9 @@ namespace iterator {
       _base += n;
       return *this;
     }
+    difference_type operator-(indexed_iterator const & it) const {
+      return _base - it._base;
+    }
     indexed_iterator operator-(difference_type n) const {
       return indexed_iterator{*this} -= n;
     }

+ 47 - 0
test/end_aware_iterator_test.cxx

@@ -7,6 +7,21 @@
 using end_aware_iterator =
     ::iterator::end_aware_iterator<std::vector<int>::iterator>;
 
+TEST(EndAwareIteratorTest, IsConstructedFromInputs) {
+  std::vector<int> v{1, 2, 3, 4, 5};
+  auto begin = v.begin(), end = v.end();
+  end_aware_iterator eai(begin, end);
+  EXPECT_THAT(eai.current(), begin);
+  EXPECT_THAT(eai.end(), end);
+}
+
+// TODO: This ought to be implemented as a compiles-test
+TEST(EndAwareIteratorTest, CanCastCompatibleIterators) {
+  std::vector<int> v{1, 2, 3, 4, 5};
+  end_aware_iterator eai(v.begin(), v.end());
+  ::iterator::end_aware_iterator<std::vector<int>::const_iterator>{eai};
+}
+
 TEST(EndAwareIteratorTest, BeginWrapperIsEqualToBegin) {
   std::vector<int> v{1, 2, 3, 4, 5};
   EXPECT_THAT(*v.begin(), *end_aware_iterator(v.begin(), v.end()));
@@ -27,6 +42,38 @@ TEST(EndAwareIteratorTest, CanTellYouThatItsReachedEnd) {
   EXPECT_TRUE(it.done());
 }
 
+TEST(EndAwareIteratorTest, SingleArgIsEnd) {
+  std::vector<int> v{1, 2, 3, 4, 5};
+  EXPECT_TRUE(end_aware_iterator(v.begin()).done());
+}
+
+TEST(EndAwareIteratorTest, EmptyIteratorIsEnd) {
+  EXPECT_TRUE(end_aware_iterator().done());
+}
+
+TEST(EndAwareIteratorTest, AllEndPointsAreEqual) {
+  std::vector<int> v1{1, 2, 3, 4, 5};
+  std::vector<int> v2{8, 9, 10};
+  EXPECT_THAT(end_aware_iterator(v1.end(), v1.end()),
+              end_aware_iterator(v2.end(), v2.end()));
+}
+
+TEST(EndAwareIteratorTest, PreIncrementAdvancesIterator) {
+  std::vector<int> v{1, 2, 3, 4, 5};
+  end_aware_iterator eai(v.begin(), v.end());
+  EXPECT_THAT(*eai, 1);
+  EXPECT_THAT(*++eai, 2);
+  EXPECT_THAT(*eai, 2);
+}
+
+TEST(EndAwareIteratorTest, PostIncrementReturnsCopyOfPrev) {
+  std::vector<int> v{1, 2, 3, 4, 5};
+  end_aware_iterator eai(v.begin(), v.end());
+  EXPECT_THAT(*eai, 1);
+  EXPECT_THAT(*eai++, 1);
+  EXPECT_THAT(*eai, 2);
+}
+
 TEST(EndAwareIteratorTest, IncrementOnEndIsNoOp) {
   std::vector<int> v{1, 2, 3, 4, 5};
   end_aware_iterator it{v.end(), v.end()};

+ 30 - 13
test/indexed_iterator_test.cxx

@@ -4,21 +4,38 @@
 
 #include <gmock/gmock.h>
 
-using idx_iterator =
-    iterator::indexed_iterator<std::vector<int>::const_iterator>;
+using idx_iterator = iterator::indexed_iterator<std::vector<int>::iterator>;
+
+// TODO: This ought to be implemented as a compiles-test
+TEST(IndexedIteratorTest, CanCastCompatibleIterators) {
+  std::vector<int> v{1, 2, 3, 4, 5};
+  idx_iterator eai(v.begin());
+  ::iterator::indexed_iterator<std::vector<int>::const_iterator>{eai};
+}
+
+TEST(IndexedIteratorTest, CanLieAboutIndex) {
+  std::vector<int> vec{5, 3, 2, 8, 9, 11, 2, 4};
+  idx_iterator it(vec.begin(), 3);
+  EXPECT_THAT(it->first, 3);
+}
+
+TEST(IndexedIteratorTest, FakeIndexDoesntEffectEqualityCheck) {
+  std::vector<int> vec{5, 3, 2, 8, 9, 11, 2, 4};
+  EXPECT_THAT(idx_iterator(vec.begin()), idx_iterator(vec.begin(), 3));
+}
 
 TEST(IndexedIteratorTest, TreatsVectorIteratorAsMapIdxToValue) {
-  std::vector<int> const vec{5, 3, 2, 8, 9, 11, 2, 4};
-  idx_iterator it{vec.begin()}, end{vec.end()};
-  for (; it != end; ++it) {
-    EXPECT_THAT((*it).second, vec[(*it).first]);
-  }
+  std::vector<int> vec{5, 3, 2, 8, 9, 11, 2, 4};
+  std::vector<std::pair<int, int>> const expected{
+      {0, 5}, {1, 3}, {2, 2}, {3, 8}, {4, 9}, {5, 11}, {6, 2}, {7, 4}};
+
+  std::vector<std::pair<int, int>> const result(idx_iterator(vec.begin()),
+                                                idx_iterator(vec.end()));
+  EXPECT_THAT(result, expected);
 }
 
-TEST(IndexedIteratorTest, ContainsReferencesToContainersElements) {
-  std::vector<int> const vec{5, 3, 2, 8, 9, 11, 2, 4};
-  idx_iterator it{vec.begin()}, end{vec.end()};
-  for (; it != end; ++it) {
-    EXPECT_THAT(&(*it).second, &vec[(*it).first]);
-  }
+TEST(IndexedIteratorTest, CanMutatePointedToData) {
+  std::vector<int> vec{5, 3, 2, 8, 9, 11, 2, 4};
+  idx_iterator(vec.begin() + 4, 4)->second = -1;
+  EXPECT_THAT(vec[4], -1);
 }

+ 6 - 0
test/join_iterator_test.cxx

@@ -24,6 +24,12 @@ TEST(JoinIteratorTest, MovesFromListToListWhenReachingEnd) {
   EXPECT_THAT(*++it, mv[1][0]);
 }
 
+TEST(JoinIteratorTest, SkipsOverEmptyElements) {
+  std::vector<std::vector<int>> mv{{1, 2, 3}, {}, {4, 5, 6}};
+  join_iterator it(make_end_aware_iterator(mv), {mv[0].end(), mv[0].end()});
+  EXPECT_THAT(*++it, mv[2][0]);
+}
+
 TEST(JoinIteratorTest, IncrementEndIsNoOp) {
   std::vector<std::vector<int>> mv{{1, 2, 3}, {4, 5, 6}};
   join_iterator it({mv.end(), mv.end()}, {mv.back().end(), mv.back().end()});