[iter] Implement range-based for loops
authorBehdad Esfahbod <behdad@behdad.org>
Tue, 7 May 2019 02:57:15 +0000 (19:57 -0700)
committerBehdad Esfahbod <behdad@behdad.org>
Tue, 7 May 2019 02:57:35 +0000 (19:57 -0700)
Part of https://github.com/harfbuzz/harfbuzz/issues/1648

src/hb-array.hh
src/hb-iter.hh
src/hb-ot-layout-common.hh
src/hb-set.hh
src/test-iter.cc

index b4619ee..37ca63d 100644 (file)
@@ -80,6 +80,8 @@ struct hb_array_t : hb_iter_with_fallback_t<hb_array_t<Type>, Type&>
     length -= n;
   }
   unsigned __len__ () const { return length; }
+  bool operator != (const hb_array_t& o) const
+  { return arrayZ != o.arrayZ || length != o.length; }
 
   /* Extra operators.
    */
@@ -224,6 +226,10 @@ struct hb_sorted_array_t :
   hb_sorted_array_t& operator = (const hb_array_t<U> &o)
   { hb_array_t<Type> (*this) = o; return *this; }
 
+  /* Iterator implementation. */
+  bool operator != (const hb_sorted_array_t& o) const
+  { return this->arrayZ != o.arrayZ || this->length != o.length; }
+
   hb_sorted_array_t<Type> sub_array (unsigned int start_offset, unsigned int *seg_count /* IN/OUT */) const
   { return hb_sorted_array_t<Type> (((const hb_array_t<Type> *) (this))->sub_array (start_offset, seg_count)); }
   hb_sorted_array_t<Type> sub_array (unsigned int start_offset, unsigned int seg_count) const
index 6fe984f..3240fc3 100644 (file)
  * copied by value.  If the collection / object being iterated on
  * is writable, then the iterator returns lvalues, otherwise it
  * returns rvalues.
+ *
+ * TODO Document more.
+ *
+ * If iterator implementation implements operator!=, then can be
+ * used in range-based for loop.  That comes free if the iterator
+ * is random-access.  Otherwise, the range-based for loop incurs
+ * one traversal to find end(), which can be avoided if written
+ * as a while-style for loop, or if iterator implements a faster
+ * __end__() method.
+ * TODO When opting in for C++17, address this by changing return
+ * type of .end()?
  */
 
 
@@ -72,10 +83,13 @@ struct hb_iter_t
   /* Operators. */
   iter_t iter () const { return *thiz(); }
   iter_t operator + () const { return *thiz(); }
+  iter_t begin () const { return *thiz(); }
+  iter_t end () const { return thiz()->__end__ (); }
   explicit operator bool () const { return thiz()->__more__ (); }
   unsigned len () const { return thiz()->__len__ (); }
   /* The following can only be enabled if item_t is reference type.  Otherwise
-   * it will be returning pointer to temporary rvalue. */
+   * it will be returning pointer to temporary rvalue.
+   * TODO Use a wrapper return type to fix for non-reference type. */
   template <typename T = item_t,
            hb_enable_if (hb_is_reference (T))>
   hb_remove_reference<item_t>* operator -> () const { return hb_addressof (**thiz()); }
@@ -107,6 +121,8 @@ struct hb_iter_t
 
 #define HB_ITER_USING(Name) \
   using item_t = typename Name::item_t; \
+  using Name::begin; \
+  using Name::end; \
   using Name::item_size; \
   using Name::is_iterator; \
   using Name::iter; \
@@ -150,7 +166,6 @@ struct
 
 } HB_FUNCOBJ (hb_iter);
 
-
 /* Mixin to fill in what the subclass doesn't provide. */
 template <typename iter_t, typename item_t = typename iter_t::__item_t__>
 struct hb_iter_fallback_mixin_t
@@ -178,6 +193,18 @@ struct hb_iter_fallback_mixin_t
   void __prev__ () { *thiz() -= 1; }
   void __rewind__ (unsigned n) { while (n--) --*thiz(); }
 
+  /* Range-based for: Implement __end__() if can be done faster,
+   * and operator!=. */
+  iter_t __end__ () const
+  {
+    if (thiz()->is_random_access_iterator)
+      return *thiz() + thiz()->len ();
+    /* Above expression loops twice. Following loops once. */
+    auto it = *thiz();
+    while (it) ++it;
+    return it;
+  }
+
   protected:
   hb_iter_fallback_mixin_t () {}
   hb_iter_fallback_mixin_t (const hb_iter_fallback_mixin_t &o HB_UNUSED) {}
@@ -250,6 +277,31 @@ struct hb_is_iterator_of { enum {
   hb_is_sorted_iterator_of (Iter, typename Iter::item_t)
 
 
+/* Range-based 'for' for iterables. */
+
+template <typename Iterable,
+         hb_enable_if (hb_is_iterable (Iterable))>
+static inline auto begin (Iterable&& iterable) HB_AUTO_RETURN (hb_iter (iterable).begin ())
+
+template <typename Iterable,
+         hb_enable_if (hb_is_iterable (Iterable))>
+static inline auto end (Iterable&& iterable) HB_AUTO_RETURN (hb_iter (iterable).end ())
+
+/* begin()/end() are NOT looked up non-ADL.  So each namespace must declare them.
+ * Do it for namespace OT. */
+namespace OT {
+
+template <typename Iterable,
+         hb_enable_if (hb_is_iterable (Iterable))>
+static inline auto begin (Iterable&& iterable) HB_AUTO_RETURN (hb_iter (iterable).begin ())
+
+template <typename Iterable,
+         hb_enable_if (hb_is_iterable (Iterable))>
+static inline auto end (Iterable&& iterable) HB_AUTO_RETURN (hb_iter (iterable).end ())
+
+}
+
+
 /*
  * Adaptors, combiners, etc.
  */
@@ -279,6 +331,8 @@ struct hb_map_iter_t :
   void __forward__ (unsigned n) { it += n; }
   void __prev__ () { --it; }
   void __rewind__ (unsigned n) { it -= n; }
+  bool operator != (const hb_map_iter_t& o) const
+  { return it != o.it || f != o.f; }
 
   private:
   Iter it;
@@ -322,6 +376,8 @@ struct hb_filter_iter_t :
   bool __more__ () const { return bool (it); }
   void __next__ () { do ++it; while (it && !p (f (*it))); }
   void __prev__ () { --it; }
+  bool operator != (const hb_filter_iter_t& o) const
+  { return it != o.it || p != o.p || f != o.f; }
 
   private:
   Iter it;
@@ -407,6 +463,9 @@ struct hb_zip_iter_t :
   void __forward__ (unsigned n) { a += n; b += n; }
   void __prev__ () { --a; --b; }
   void __rewind__ (unsigned n) { a -= n; b -= n; }
+  hb_zip_iter_t __end__ () const { return hb_zip_iter_t (a.end (), b.end ()); }
+  bool operator != (const hb_zip_iter_t& o) const
+  { return a != o.a || b != o.b; }
 
   private:
   A a;
@@ -442,6 +501,17 @@ struct hb_enumerate_iter_t :
   void __forward__ (unsigned n) { i += n; it += n; }
   void __prev__ () { --i; --it; }
   void __rewind__ (unsigned n) { i -= n; it -= n; }
+  hb_enumerate_iter_t __end__ () const
+  {
+    if (is_random_access_iterator)
+      return *this + this->len ();
+    /* Above expression loops twice. Following loops once. */
+    auto it = *this;
+    while (it) ++it;
+    return it;
+  }
+  bool operator != (const hb_enumerate_iter_t& o) const
+  { return i != o.i || it != o.it; }
 
   private:
   unsigned i;
index 09e7711..a527b39 100644 (file)
@@ -835,6 +835,8 @@ struct CoverageFormat1
     bool more () const { return i < c->glyphArray.len; }
     void next () { i++; }
     hb_codepoint_t get_glyph () const { return c->glyphArray[i]; }
+    bool operator != (const iter_t& o) const
+    { return i != o.i || c != o.c; }
 
     private:
     const struct CoverageFormat1 *c;
@@ -987,6 +989,8 @@ struct CoverageFormat2
       j++;
     }
     hb_codepoint_t get_glyph () const { return j; }
+    bool operator != (const iter_t& o) const
+    { return i != o.i || j != o.j || c != o.c; }
 
     private:
     const struct CoverageFormat2 *c;
@@ -1136,6 +1140,16 @@ struct Coverage
       default:return 0;
       }
     }
+    bool operator != (const iter_t& o) const
+    {
+      if (format != o.format) return true;
+      switch (format)
+      {
+      case 1: return u.format1 != o.u.format1;
+      case 2: return u.format2 != o.u.format2;
+      default:return false;
+      }
+    }
 
     private:
     unsigned int format;
index 76100f6..332e07b 100644 (file)
@@ -701,6 +701,9 @@ struct hb_set_t
     void __next__ () { s->next (&v); if (l) l--; }
     void __prev__ () { s->previous (&v); }
     unsigned __len__ () const { return l; }
+    iter_t end () const { return iter_t (*s); }
+    bool operator != (const iter_t& o) const
+    { return s != o.s || v != o.v; }
 
     protected:
     const hb_set_t *s;
index 01ec93a..8f2fc80 100644 (file)
@@ -43,6 +43,7 @@ struct array_iter_t : hb_iter_with_fallback_t<array_iter_t<T>, T&>
   void __forward__ (unsigned n) { arr += n; }
   void __rewind__ (unsigned n) { arr -= n; }
   unsigned __len__ () const { return arr.length; }
+  bool operator != (const array_iter_t& o) { return arr != o.arr; }
 
   private:
   hb_array_t<T> arr;
@@ -66,12 +67,8 @@ struct some_array_t
 template <typename Iter,
          hb_enable_if (hb_is_iterator (Iter))>
 static void
-test_iterator (Iter it)
+test_iterator_non_default_constructable (Iter it)
 {
-  Iter default_constructed;
-
-  assert (!default_constructed);
-
   /* Iterate over a copy of it. */
   for (auto c = it.iter (); c; c++)
     *c;
@@ -80,6 +77,10 @@ test_iterator (Iter it)
   for (auto c = +it; c; c++)
     *c;
 
+  /* Range-based for over a copy. */
+  for (auto _ : +it)
+    (void) _;
+
   it += it.len ();
   it = it + 10;
   it = 10 + it;
@@ -90,11 +91,25 @@ test_iterator (Iter it)
   static_assert (true || it.is_sorted_iterator, "");
 }
 
+template <typename Iter,
+         hb_enable_if (hb_is_iterator (Iter))>
+static void
+test_iterator (Iter it)
+{
+  Iter default_constructed;
+  assert (!default_constructed);
+
+  test_iterator_non_default_constructable (it);
+}
+
 template <typename Iterable,
          hb_enable_if (hb_is_iterable (Iterable))>
 static void
 test_iterable (const Iterable &lst = Null(Iterable))
 {
+  for (auto _ : lst)
+    (void) _;
+
   // Test that can take iterator from.
   test_iterator (lst.iter ());
 }
@@ -141,6 +156,9 @@ main (int argc, char **argv)
   test_iterable<OT::Coverage> ();
 
   test_iterator (hb_zip (st, v));
+  test_iterator_non_default_constructable (hb_enumerate (st));
+  //test_iterator_non_default_constructable (hb_iter (st) | hb_filter ());
+  //test_iterator_non_default_constructable (hb_iter (st) | hb_map (hb_identity));
 
   hb_any (st);