[iter] Implement range-based for loops

Part of https://github.com/harfbuzz/harfbuzz/issues/1648
This commit is contained in:
Behdad Esfahbod 2019-05-06 19:57:15 -07:00
parent e261dc3a40
commit 4c2fd05ca5
5 changed files with 118 additions and 7 deletions

View File

@ -80,6 +80,8 @@ struct hb_array_t : hb_iter_with_fallback_t<hb_array_t<Type>, Type&>
length -= n; length -= n;
} }
unsigned __len__ () const { return length; } unsigned __len__ () const { return length; }
bool operator != (const hb_array_t& o) const
{ return arrayZ != o.arrayZ || length != o.length; }
/* Extra operators. /* Extra operators.
*/ */
@ -224,6 +226,10 @@ struct hb_sorted_array_t :
hb_sorted_array_t& operator = (const hb_array_t<U> &o) hb_sorted_array_t& operator = (const hb_array_t<U> &o)
{ hb_array_t<Type> (*this) = o; return *this; } { 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 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)); } { 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 hb_sorted_array_t<Type> sub_array (unsigned int start_offset, unsigned int seg_count) const

View File

@ -42,6 +42,17 @@
* copied by value. If the collection / object being iterated on * copied by value. If the collection / object being iterated on
* is writable, then the iterator returns lvalues, otherwise it * is writable, then the iterator returns lvalues, otherwise it
* returns rvalues. * 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. */ /* Operators. */
iter_t iter () const { return *thiz(); } iter_t iter () const { return *thiz(); }
iter_t operator + () 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__ (); } explicit operator bool () const { return thiz()->__more__ (); }
unsigned len () const { return thiz()->__len__ (); } unsigned len () const { return thiz()->__len__ (); }
/* The following can only be enabled if item_t is reference type. Otherwise /* 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, template <typename T = item_t,
hb_enable_if (hb_is_reference (T))> hb_enable_if (hb_is_reference (T))>
hb_remove_reference<item_t>* operator -> () const { return hb_addressof (**thiz()); } hb_remove_reference<item_t>* operator -> () const { return hb_addressof (**thiz()); }
@ -107,6 +121,8 @@ struct hb_iter_t
#define HB_ITER_USING(Name) \ #define HB_ITER_USING(Name) \
using item_t = typename Name::item_t; \ using item_t = typename Name::item_t; \
using Name::begin; \
using Name::end; \
using Name::item_size; \ using Name::item_size; \
using Name::is_iterator; \ using Name::is_iterator; \
using Name::iter; \ using Name::iter; \
@ -150,7 +166,6 @@ struct
} HB_FUNCOBJ (hb_iter); } HB_FUNCOBJ (hb_iter);
/* Mixin to fill in what the subclass doesn't provide. */ /* Mixin to fill in what the subclass doesn't provide. */
template <typename iter_t, typename item_t = typename iter_t::__item_t__> template <typename iter_t, typename item_t = typename iter_t::__item_t__>
struct hb_iter_fallback_mixin_t struct hb_iter_fallback_mixin_t
@ -178,6 +193,18 @@ struct hb_iter_fallback_mixin_t
void __prev__ () { *thiz() -= 1; } void __prev__ () { *thiz() -= 1; }
void __rewind__ (unsigned n) { while (n--) --*thiz(); } 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: protected:
hb_iter_fallback_mixin_t () {} hb_iter_fallback_mixin_t () {}
hb_iter_fallback_mixin_t (const hb_iter_fallback_mixin_t &o HB_UNUSED) {} 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) 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. * Adaptors, combiners, etc.
*/ */
@ -279,6 +331,8 @@ struct hb_map_iter_t :
void __forward__ (unsigned n) { it += n; } void __forward__ (unsigned n) { it += n; }
void __prev__ () { --it; } void __prev__ () { --it; }
void __rewind__ (unsigned n) { it -= n; } void __rewind__ (unsigned n) { it -= n; }
bool operator != (const hb_map_iter_t& o) const
{ return it != o.it || f != o.f; }
private: private:
Iter it; Iter it;
@ -322,6 +376,8 @@ struct hb_filter_iter_t :
bool __more__ () const { return bool (it); } bool __more__ () const { return bool (it); }
void __next__ () { do ++it; while (it && !p (f (*it))); } void __next__ () { do ++it; while (it && !p (f (*it))); }
void __prev__ () { --it; } void __prev__ () { --it; }
bool operator != (const hb_filter_iter_t& o) const
{ return it != o.it || p != o.p || f != o.f; }
private: private:
Iter it; Iter it;
@ -407,6 +463,9 @@ struct hb_zip_iter_t :
void __forward__ (unsigned n) { a += n; b += n; } void __forward__ (unsigned n) { a += n; b += n; }
void __prev__ () { --a; --b; } void __prev__ () { --a; --b; }
void __rewind__ (unsigned n) { a -= n; b -= n; } 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: private:
A a; A a;
@ -442,6 +501,17 @@ struct hb_enumerate_iter_t :
void __forward__ (unsigned n) { i += n; it += n; } void __forward__ (unsigned n) { i += n; it += n; }
void __prev__ () { --i; --it; } void __prev__ () { --i; --it; }
void __rewind__ (unsigned n) { i -= n; it -= n; } 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: private:
unsigned i; unsigned i;

View File

@ -835,6 +835,8 @@ struct CoverageFormat1
bool more () const { return i < c->glyphArray.len; } bool more () const { return i < c->glyphArray.len; }
void next () { i++; } void next () { i++; }
hb_codepoint_t get_glyph () const { return c->glyphArray[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: private:
const struct CoverageFormat1 *c; const struct CoverageFormat1 *c;
@ -987,6 +989,8 @@ struct CoverageFormat2
j++; j++;
} }
hb_codepoint_t get_glyph () const { return 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: private:
const struct CoverageFormat2 *c; const struct CoverageFormat2 *c;
@ -1136,6 +1140,16 @@ struct Coverage
default:return 0; 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: private:
unsigned int format; unsigned int format;

View File

@ -701,6 +701,9 @@ struct hb_set_t
void __next__ () { s->next (&v); if (l) l--; } void __next__ () { s->next (&v); if (l) l--; }
void __prev__ () { s->previous (&v); } void __prev__ () { s->previous (&v); }
unsigned __len__ () const { return l; } 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: protected:
const hb_set_t *s; const hb_set_t *s;

View 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 __forward__ (unsigned n) { arr += n; }
void __rewind__ (unsigned n) { arr -= n; } void __rewind__ (unsigned n) { arr -= n; }
unsigned __len__ () const { return arr.length; } unsigned __len__ () const { return arr.length; }
bool operator != (const array_iter_t& o) { return arr != o.arr; }
private: private:
hb_array_t<T> arr; hb_array_t<T> arr;
@ -66,12 +67,8 @@ struct some_array_t
template <typename Iter, template <typename Iter,
hb_enable_if (hb_is_iterator (Iter))> hb_enable_if (hb_is_iterator (Iter))>
static void 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. */ /* Iterate over a copy of it. */
for (auto c = it.iter (); c; c++) for (auto c = it.iter (); c; c++)
*c; *c;
@ -80,6 +77,10 @@ test_iterator (Iter it)
for (auto c = +it; c; c++) for (auto c = +it; c; c++)
*c; *c;
/* Range-based for over a copy. */
for (auto _ : +it)
(void) _;
it += it.len (); it += it.len ();
it = it + 10; it = it + 10;
it = 10 + it; it = 10 + it;
@ -90,11 +91,25 @@ test_iterator (Iter it)
static_assert (true || it.is_sorted_iterator, ""); 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, template <typename Iterable,
hb_enable_if (hb_is_iterable (Iterable))> hb_enable_if (hb_is_iterable (Iterable))>
static void static void
test_iterable (const Iterable &lst = Null(Iterable)) test_iterable (const Iterable &lst = Null(Iterable))
{ {
for (auto _ : lst)
(void) _;
// Test that can take iterator from. // Test that can take iterator from.
test_iterator (lst.iter ()); test_iterator (lst.iter ());
} }
@ -141,6 +156,9 @@ main (int argc, char **argv)
test_iterable<OT::Coverage> (); test_iterable<OT::Coverage> ();
test_iterator (hb_zip (st, v)); 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); hb_any (st);