[iter] Implement range-based for loops
Part of https://github.com/harfbuzz/harfbuzz/issues/1648
This commit is contained in:
parent
e261dc3a40
commit
4c2fd05ca5
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue