[iter] Implement range-based for loops
Part of https://github.com/harfbuzz/harfbuzz/issues/1648
diff --git a/src/hb-array.hh b/src/hb-array.hh
index b4619ee..37ca63d 100644
--- a/src/hb-array.hh
+++ b/src/hb-array.hh
@@ -80,6 +80,8 @@
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 @@
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
diff --git a/src/hb-iter.hh b/src/hb-iter.hh
index 6fe984f..3240fc3 100644
--- a/src/hb-iter.hh
+++ b/src/hb-iter.hh
@@ -42,6 +42,17 @@
* 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 @@
/* 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 @@
#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 @@
} 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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;
diff --git a/src/hb-ot-layout-common.hh b/src/hb-ot-layout-common.hh
index 09e7711..a527b39 100644
--- a/src/hb-ot-layout-common.hh
+++ b/src/hb-ot-layout-common.hh
@@ -835,6 +835,8 @@
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 @@
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 @@
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;
diff --git a/src/hb-set.hh b/src/hb-set.hh
index 76100f6..332e07b 100644
--- a/src/hb-set.hh
+++ b/src/hb-set.hh
@@ -701,6 +701,9 @@
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;
diff --git a/src/test-iter.cc b/src/test-iter.cc
index 01ec93a..8f2fc80 100644
--- a/src/test-iter.cc
+++ b/src/test-iter.cc
@@ -43,6 +43,7 @@
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 @@
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 @@
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 @@
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 @@
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);