Eric Niebler's pet project
Ranges TS ("Technical Specification")
<algorithm>
supports rangesnamespace ranges { template< typename Rng, typename What > decltype(auto) find( Rng && rng, What const& what ) { return std::find( ranges::begin(rng), ranges::end(rng), what ); }}
std::vector<int> v;auto it=ranges::find( v, 4); // first element of value 4.
vs.
struct A { int id; double data;};std::vector<A> v={...};auto it=ranges::find_if( v, [](A const& a){ return a.id==4; } // first element of value 4 in id);
struct A { int id; double data;};std::vector<A> v={...};auto it=ranges::find( tc::transform(v,std::mem_fn(&A::id)), 4); // first element of value 4 in id
What is it
pointing to?
int
!What if I want it
to point to A
?
auto it=ranges::find( tc::transform(v,std::mem_fn(&A::id)), 4).base();
template<typename Base, typename Func>struct transform_range { struct iterator { private: Func m_func; // in every iterator, hmmm... decltype( tc::begin(std::declval<Base&>()) ) m_it; public: decltype(auto) operator*() const { return m_func(*m_it); } decltype(auto) base() const { return (m_it); } ... };};
template<typename Base, typename Func>struct filter_range { struct iterator { private: Func m_func; // functor and TWO iterators! decltype( ranges::begin(std::declval<Base&>()) ) m_it; decltype( ranges::begin(std::declval<Base&>()) ) m_itEnd; public: iterator& operator++() { ++m_it; while( m_it!=m_itEnd && !static_cast<bool>(m_func(*m_it)) ) ++m_it; // why static_cast<bool> ? return *this; } ... };};
Index
template<typename Base, typename Func>struct index_range { ... using Index=...; Index begin_index() const; Index end_index() const; void increment_index( Index& idx ) const; void decrement_index( Index& idx ) const; reference dereference( Index& idx ) const; ...};
Index from Iterator
using Index = Iterator
Iterator from Index
template<typename IndexRng>struct iterator_for_index { IndexRng* m_rng typename IndexRng::Index m_idx; iterator& operator++() { m_rng.increment_index(m_idx); return *this; } ...};
Index-based filter_range
template<typename Base, typename Func>struct filter_range { Func m_func; Base& m_base; using Index=typename Base::Index; void increment_index( Index& idx ) const { do { m_base.increment_index(idx); } while( idx!=m_base.end_index() && !m_func(m_base.dereference_index(idx)) ); }};
Index-based filter_range
template<typename Base, typename Func>struct filter_range { Func m_func; Base& m_base; using Index=typename Base::Index;...
template<typename IndexRng>struct iterator_for_index { IndexRng* m_rng typename IndexRng::Index m_idx; ...
If adaptor input is lvalue container
tc::filter
creates viewIf adaptor input is rvalue container
tc::filter
creates containerAlways lazy
auto rng=tc::filter(vec,pred1);bool b=ranges::any_of(rng,pred2);
auto rng=tc::filter(creates_vector(),pred1);bool b=ranges::any_of(rng,pred2);
template< typename Pack, typename Rng, typename What >decltype(auto) find( Rng && rng, What const& what ) { auto const itEnd=ranges::end(rng); for( auto it=ranges::begin(rng); it!=itEnd; ++it ) if( *it==what ) return Pack::pack(it,rng); return Pack::pack_singleton(rng);}
struct return_iterator_or_end { static auto pack(auto it, auto&& rng) { return it; } static auto pack_singleton(auto && rng) { return ranges::end(rng); }}
auto it=find<return_iterator_or_end>(...)
template< typename Pack, typename Rng, typename What >decltype(auto) find( Rng && rng, What const& what ) { auto const itEnd=ranges::end(rng); for( auto it=ranges::begin(rng); it!=itEnd; ++it ) if( *it==what ) return Pack::pack(it,rng); return Pack::pack_singleton(rng);}
struct return_iterator { static auto pack(auto it, auto&& rng) { return it; } static auto pack_singleton(auto && rng) { std::assert(false); return ranges::end(rng); }}
auto it=find<return_iterator>(...)
template< typename Pack, typename Rng, typename What >decltype(auto) find( Rng && rng, What const& what ) { auto const itEnd=ranges::end(rng); for( auto it=ranges::begin(rng); it!=itEnd; ++it ) if( *it==what ) return Pack::pack(it,rng); return Pack::pack_singleton(rng);}
struct return_head { static auto pack(auto it, auto&& rng) { return tc::take( std::forward<decltype(rng)>(rng), it); } static auto pack_singleton(auto&& rng) { std::assert(false); return tc::take( std::forward<decltype(rng)>(rng), ranges::begin(rng)); }}
auto it=find<return_head>(...)
template<typename Func>void traverse_widgets( Func func ) { if( window1 ) { window1->traverse_widgets(std::ref(func)); } func(button1); func(listbox1); if( window2 ) { window2->traverse_widgets(std::ref(func)); }}
mouse_hit_any_widget=tc::any_of( [](auto func){ traverse_widgets(func); }, [](auto const& widget) { return widget.mouse_hit(); });
Consumer is at bottom of stack
Producer is at top of stack
^| Stack Consumer Consumer| / \ / \ Producer Producer Producer
Producer is at bottom of stack
Consumer is at top of stack
// does not compile, conceptualgenerator<widget&> traverse_widgets() { if( window1 ) { window1->traverse_widgets(); } yield button1; yield listbox1; if( window2 ) { window2->traverse_widgets(); }}
Algorithm | Internal Iteration? |
---|---|
find | no (single pass iterators) |
binary_search | no (random access iterators) |
for_each | yes |
accumulate | yes |
all_of | yes |
any_of | yes |
none_of | yes |
... |
Adaptor | Internal Iteration? |
---|---|
tc::filter | yes |
tc::transform | yes |
So allow ranges that support only internal iteration!
namespace tc { template< typename Rng > bool any_of( Rng const& rng ) { bool bResult=false; tc::enumerate( rng, [&](bool_context b){ bResult=bResult || b; } ); return bResult; }}
tc::enumerate
is common interface for iterator, index and generator ranges
Ok?
First idea: exception!
Second idea:
enum break_or_continue { break_, continue_};
template< typename Rng >bool any_of( Rng const& rng ) { bool bResult=false; tc::enumerate( rng, [&](bool_context b){ bResult=bResult || b; return bResult ? break_ : continue_; } ); return bResult;}
... auto dereference_index(index const& idx) const { return idx.switch( [&](Index1 const& idx1){ return m_rng1.dereference(idx1); }, [&](Index2 const& idx2){ return m_rng2.dereference(idx2); } ); } ...};
... auto dereference_index(index const& idx) const { return idx.switch( [&](Index1 const& idx1){ return m_rng1.dereference(idx1); }, [&](Index2 const& idx2){ return m_rng2.dereference(idx2); } ); } ...};
template<typename Rng1, typename Rng2>struct concat_range {private: Rng1 m_rng1; Rng2 m_rng2;public: ... // version for non-breaking func template<typename Func> void operator()(Func func) { tc::fn_enumerate(m_rng1, func); tc::fn_enumerate(m_rng2, func); }};
Now that we have all this range stuff
Now that we have all this range stuff
Now that we have all this range stuff
because it encourages people to write this
bool b=false;for( int n : rng ) { if( is_prime(n) ) { b=true; break; }}
Now that we have all this range stuff
because it encourages people to write this
bool b=false;for( int n : rng ) { if( is_prime(n) ) { b=true; break; }}
instead of this
bool b=tc::any_of( rng, is_prime );
THANK YOU!
Keyboard shortcuts
↑, ←, Pg Up, k | Go to previous slide |
↓, →, Pg Dn, Space, j | Go to next slide |
Home | Go to first slide |
End | Go to last slide |
b / m / f | Toggle blackout / mirrored / fullscreen mode |
c | Clone slideshow |
p | Toggle presenter mode |
t | Restart the presentation timer |
?, h | Toggle this help |
Esc | Back to slideshow |