Repository: dtolnay/cxx
Pull request #599 (2020-12-27 20:45:15)

Expand Slice iterator to be random access iterator
https://en.cppreference.com/w/cpp/named_req/RandomAccessIterator


Patch:
diff --git a/book/src/binding/slice.md b/book/src/binding/slice.md
index ef9450c0..20ee98f0 100644
--- a/book/src/binding/slice.md
+++ b/book/src/binding/slice.md
@@ -40,16 +40,32 @@ public:
 # template <typename T>
 # class Slice<T>::iterator final {
 # public:
+#   using iterator_category = std::random_access_iterator_tag;
 #   using value_type = T;
 #   using pointer = T *;
 #   using reference = T &;
 #
 #   T &operator*() const noexcept;
 #   T *operator->() const noexcept;
+#   T &operator[](ptrdiff_t) const noexcept;
+#
 #   iterator &operator++() noexcept;
 #   iterator operator++(int) noexcept;
+#   iterator &operator--() noexcept;
+#   iterator operator--(int) noexcept;
+#
+#   iterator &operator+=(ptrdiff_t) noexcept;
+#   iterator &operator-=(ptrdiff_t) noexcept;
+#   iterator operator+(ptrdiff_t) const noexcept;
+#   iterator operator-(ptrdiff_t) const noexcept;
+#   ptrdiff_t operator-(const iterator &) const noexcept;
+#
 #   bool operator==(const iterator &) const noexcept;
 #   bool operator!=(const iterator &) const noexcept;
+#   bool operator<(const iterator &) const noexcept;
+#   bool operator>(const iterator &) const noexcept;
+#   bool operator<=(const iterator &) const noexcept;
+#   bool operator>=(const iterator &) const noexcept;
 # };
 #
 # } // namespace rust
diff --git a/include/cxx.h b/include/cxx.h
index c1d452e3..61a500f4 100644
--- a/include/cxx.h
+++ b/include/cxx.h
@@ -182,18 +182,33 @@ private:
 template <typename T>
 class Slice<T>::iterator final {
 public:
-  using difference_type = std::ptrdiff_t;
+  using iterator_category = std::random_access_iterator_tag;
   using value_type = T;
+  using difference_type = std::ptrdiff_t;
   using pointer = typename std::add_pointer<T>::type;
   using reference = typename std::add_lvalue_reference<T>::type;
-  using iterator_category = std::forward_iterator_tag;
 
-  T &operator*() const noexcept;
-  T *operator->() const noexcept;
+  reference operator*() const noexcept;
+  pointer operator->() const noexcept;
+  reference operator[](difference_type) const noexcept;
+
   iterator &operator++() noexcept;
   iterator operator++(int) noexcept;
+  iterator &operator--() noexcept;
+  iterator operator--(int) noexcept;
+
+  iterator &operator+=(difference_type) noexcept;
+  iterator &operator-=(difference_type) noexcept;
+  iterator operator+(difference_type) const noexcept;
+  iterator operator-(difference_type) const noexcept;
+  difference_type operator-(const iterator &) const noexcept;
+
   bool operator==(const iterator &) const noexcept;
   bool operator!=(const iterator &) const noexcept;
+  bool operator<(const iterator &) const noexcept;
+  bool operator<=(const iterator &) const noexcept;
+  bool operator>(const iterator &) const noexcept;
+  bool operator>=(const iterator &) const noexcept;
 
 private:
   friend class Slice;
@@ -557,15 +572,23 @@ T &Slice<T>::back() const noexcept {
 }
 
 template <typename T>
-T &Slice<T>::iterator::operator*() const noexcept {
+typename Slice<T>::iterator::reference
+Slice<T>::iterator::operator*() const noexcept {
   return *this->pos;
 }
 
 template <typename T>
-T *Slice<T>::iterator::operator->() const noexcept {
+typename Slice<T>::iterator::pointer
+Slice<T>::iterator::operator->() const noexcept {
   return this->pos;
 }
 
+template <typename T>
+typename Slice<T>::iterator::reference Slice<T>::iterator::operator[](
+    typename Slice<T>::iterator::difference_type n) const noexcept {
+  return this->pos[n];
+}
+
 template <typename T>
 typename Slice<T>::iterator &Slice<T>::iterator::operator++() noexcept {
   ++this->pos;
@@ -579,6 +602,51 @@ typename Slice<T>::iterator Slice<T>::iterator::operator++(int) noexcept {
   return ret;
 }
 
+template <typename T>
+typename Slice<T>::iterator &Slice<T>::iterator::operator--() noexcept {
+  --this->pos;
+  return *this;
+}
+
+template <typename T>
+typename Slice<T>::iterator Slice<T>::iterator::operator--(int) noexcept {
+  auto ret = iterator(*this);
+  --this->pos;
+  return ret;
+}
+
+template <typename T>
+typename Slice<T>::iterator &Slice<T>::iterator::operator+=(
+    typename Slice<T>::iterator::difference_type n) noexcept {
+  this->pos += n;
+  return *this;
+}
+
+template <typename T>
+typename Slice<T>::iterator &Slice<T>::iterator::operator-=(
+    typename Slice<T>::iterator::difference_type n) noexcept {
+  this->pos -= n;
+  return *this;
+}
+
+template <typename T>
+typename Slice<T>::iterator Slice<T>::iterator::operator+(
+    typename Slice<T>::iterator::difference_type n) const noexcept {
+  return iterator(*this) += n;
+}
+
+template <typename T>
+typename Slice<T>::iterator Slice<T>::iterator::operator-(
+    typename Slice<T>::iterator::difference_type n) const noexcept {
+  return iterator(*this) -= n;
+}
+
+template <typename T>
+typename Slice<T>::iterator::difference_type
+Slice<T>::iterator::operator-(const iterator &other) const noexcept {
+  return std::distance(other.pos, this->pos);
+}
+
 template <typename T>
 bool Slice<T>::iterator::operator==(const iterator &other) const noexcept {
   return this->pos == other.pos;
@@ -589,6 +657,26 @@ bool Slice<T>::iterator::operator!=(const iterator &other) const noexcept {
   return this->pos != other.pos;
 }
 
+template <typename T>
+bool Slice<T>::iterator::operator<(const iterator &other) const noexcept {
+  return this->pos < other.pos;
+}
+
+template <typename T>
+bool Slice<T>::iterator::operator<=(const iterator &other) const noexcept {
+  return this->pos <= other.pos;
+}
+
+template <typename T>
+bool Slice<T>::iterator::operator>(const iterator &other) const noexcept {
+  return this->pos > other.pos;
+}
+
+template <typename T>
+bool Slice<T>::iterator::operator>=(const iterator &other) const noexcept {
+  return this->pos >= other.pos;
+}
+
 template <typename T>
 typename Slice<T>::iterator Slice<T>::begin() const noexcept {
   iterator it;


Test patch:
diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs
index 7504704b..df5c2794 100644
--- a/tests/ffi/lib.rs
+++ b/tests/ffi/lib.rs
@@ -120,6 +120,7 @@ pub mod ffi {
         fn c_take_str(s: &str);
         fn c_take_slice_char(s: &[c_char]);
         fn c_take_slice_shared(s: &[Shared]);
+        fn c_take_slice_shared_sort(s: &mut [Shared]);
         fn c_take_rust_string(s: String);
         fn c_take_unique_ptr_string(s: UniquePtr<CxxString>);
         fn c_take_unique_ptr_vector_u8(v: UniquePtr<CxxVector<u8>>);
diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc
index 05ff4b87..2bb7d71f 100644
--- a/tests/ffi/tests.cc
+++ b/tests/ffi/tests.cc
@@ -280,6 +280,15 @@ void c_take_slice_shared(rust::Slice<const Shared> s) {
   }
 }
 
+void c_take_slice_shared_sort(rust::Slice<Shared> s) {
+  // Exercise requirements of RandomAccessIterator.
+  // https://en.cppreference.com/w/cpp/named_req/RandomAccessIterator
+  std::sort(s.begin(), s.end());
+  if (s[0].z == 0 && s[1].z == 2 && s[2].z == 4 && s[3].z == 7) {
+    cxx_test_suite_set_correct();
+  }
+}
+
 void c_take_rust_string(rust::String s) {
   if (std::string(s) == "2020") {
     cxx_test_suite_set_correct();
diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h
index 51ab51df..50a80054 100644
--- a/tests/ffi/tests.h
+++ b/tests/ffi/tests.h
@@ -123,6 +123,7 @@ void c_take_ref_ns_c(const ::H::H &h);
 void c_take_str(rust::Str s);
 void c_take_slice_char(rust::Slice<const char> s);
 void c_take_slice_shared(rust::Slice<const Shared> s);
+void c_take_slice_shared_sort(rust::Slice<Shared> s);
 void c_take_rust_string(rust::String s);
 void c_take_unique_ptr_string(std::unique_ptr<std::string> s);
 void c_take_unique_ptr_vector_u8(std::unique_ptr<std::vector<uint8_t>> v);
diff --git a/tests/test.rs b/tests/test.rs
index 60bf1bdb..0620a9ca 100644
--- a/tests/test.rs
+++ b/tests/test.rs
@@ -125,6 +125,17 @@ fn test_c_take() {
         ffi::Shared { z: 2020 },
         ffi::Shared { z: 2021 },
     ]));
+    let shared_sort_slice = &mut [
+        ffi::Shared { z: 2 },
+        ffi::Shared { z: 0 },
+        ffi::Shared { z: 7 },
+        ffi::Shared { z: 4 },
+    ];
+    check!(ffi::c_take_slice_shared_sort(shared_sort_slice));
+    assert_eq!(shared_sort_slice[0].z, 0);
+    assert_eq!(shared_sort_slice[1].z, 2);
+    assert_eq!(shared_sort_slice[2].z, 4);
+    assert_eq!(shared_sort_slice[3].z, 7);
     check!(ffi::c_take_rust_string("2020".to_owned()));
     check!(ffi::c_take_unique_ptr_string(
         ffi::c_return_unique_ptr_string()