// Copyright 2014 Neil Groves // // Copyright (c) 2010 Ilya Murav'jov // // Use, modification and distribution is subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // // Credits: // My (Neil's) first indexed adaptor was hindered by having the underlying // iterator return the same reference as the wrapped iterator. This meant that // to obtain the index one had to get to the index_iterator and call the // index() function on it. Ilya politely pointed out that this was useless in // a number of scenarios since one naturally hides the use of iterators in // good range-based code. Ilya provided a new interface (which has remained) // and a first implementation. Much of this original implementation has // been simplified and now supports more compilers and platforms. // #ifndef BOOST_RANGE_ADAPTOR_INDEXED_HPP_INCLUDED #define BOOST_RANGE_ADAPTOR_INDEXED_HPP_INCLUDED #include #include #include #include #include #include #include #include #include #include #include #include namespace boost { namespace adaptors { struct indexed { explicit indexed(std::ptrdiff_t x = 0) : val(x) { } std::ptrdiff_t val; }; } // namespace adaptors namespace range { // Why yet another "pair" class: // - std::pair can't store references // - no need for typing for index type (default to "std::ptrdiff_t"); this is // useful in BOOST_FOREACH() expressions that have pitfalls with commas // ( see http://www.boost.org/doc/libs/1_44_0/doc/html/foreach/pitfalls.html ) // - meaningful access functions index(), value() template class index_value : public tuple { typedef tuple base_t; template struct iv_types { typedef typename tuples::element::type n_type; typedef typename tuples::access_traits::non_const_type non_const_type; typedef typename tuples::access_traits::const_type const_type; }; public: typedef typename iv_types<0>::non_const_type index_type; typedef typename iv_types<0>::const_type const_index_type; typedef typename iv_types<1>::non_const_type value_type; typedef typename iv_types<1>::const_type const_value_type; index_value() { } index_value(typename tuples::access_traits::parameter_type t0, typename tuples::access_traits::parameter_type t1) : base_t(t0, t1) { } // member functions index(), value() (non-const and const) index_type index() { return boost::tuples::get<0>(*this); } const_index_type index() const { return boost::tuples::get<0>(*this); } value_type value() { return boost::tuples::get<1>(*this); } const_value_type value() const { return boost::tuples::get<1>(*this); } }; } // namespace range namespace range_detail { template struct indexed_iterator_value_type { typedef ::boost::range::index_value< typename iterator_reference::type, typename iterator_difference::type > type; }; // Meta-function to get the traversal for the range and therefore iterator // returned by the indexed adaptor for a specified iterator type. // // Random access -> Random access // Bidirectional -> Forward // Forward -> Forward // SinglePass -> SinglePass // // The rationale for demoting a Bidirectional input to Forward is that the end // iterator cannot cheaply have an index computed for it. Therefore I chose to // demote to forward traversal. I can maintain the ability to traverse randomly // when the input is Random Access since the index for the end iterator is cheap // to compute. template struct indexed_traversal { private: typedef typename iterator_traversal::type wrapped_traversal; public: typedef typename mpl::if_< is_convertible, random_access_traversal_tag, typename mpl::if_< is_convertible, forward_traversal_tag, wrapped_traversal >::type >::type type; }; template class indexed_iterator : public iterator_facade< indexed_iterator, typename indexed_iterator_value_type::type, typename indexed_traversal::type, typename indexed_iterator_value_type::type, typename iterator_difference::type > { public: typedef Iter wrapped; private: typedef iterator_facade< indexed_iterator, typename indexed_iterator_value_type::type, typename indexed_traversal::type, typename indexed_iterator_value_type::type, typename iterator_difference::type > base_t; public: typedef typename base_t::difference_type difference_type; typedef typename base_t::reference reference; typedef typename base_t::difference_type index_type; indexed_iterator() : m_it() , m_index() { } template indexed_iterator( const indexed_iterator& other, typename enable_if >::type* = 0 ) : m_it(other.get()) , m_index(other.get_index()) { } explicit indexed_iterator(wrapped it, index_type index) : m_it(it) , m_index(index) { } wrapped get() const { return m_it; } index_type get_index() const { return m_index; } private: friend class boost::iterator_core_access; reference dereference() const { return reference(m_index, *m_it); } bool equal(const indexed_iterator& other) const { return m_it == other.m_it; } void increment() { ++m_index; ++m_it; } void decrement() { BOOST_ASSERT_MSG(m_index > 0, "indexed Iterator out of bounds"); --m_index; --m_it; } void advance(index_type n) { m_index += n; BOOST_ASSERT_MSG(m_index >= 0, "indexed Iterator out of bounds"); m_it += n; } difference_type distance_to(const indexed_iterator& other) const { return other.m_it - m_it; } wrapped m_it; index_type m_index; }; template struct indexed_range : iterator_range< indexed_iterator< typename range_iterator::type > > { typedef iterator_range< indexed_iterator< typename range_iterator::type > > base_t; BOOST_RANGE_CONCEPT_ASSERT(( boost::SinglePassRangeConcept)); public: typedef indexed_iterator< typename range_iterator::type > iterator; // Constructor for non-random access iterators. // This sets the end iterator index to i despite this being incorrect it // is never observable since bidirectional iterators are demoted to // forward iterators. indexed_range( typename base_t::difference_type i, SinglePassRange& r, single_pass_traversal_tag ) : base_t(iterator(boost::begin(r), i), iterator(boost::end(r), i)) { } indexed_range( typename base_t::difference_type i, SinglePassRange& r, random_access_traversal_tag ) : base_t(iterator(boost::begin(r), i), iterator(boost::end(r), i + boost::size(r))) { } }; } // namespace range_detail using range_detail::indexed_range; namespace adaptors { template inline indexed_range operator|(SinglePassRange& r, indexed e) { BOOST_RANGE_CONCEPT_ASSERT(( boost::SinglePassRangeConcept )); return indexed_range( e.val, r, typename range_traversal::type()); } template inline indexed_range operator|(const SinglePassRange& r, indexed e) { BOOST_RANGE_CONCEPT_ASSERT(( boost::SinglePassRangeConcept )); return indexed_range( e.val, r, typename range_traversal::type()); } template inline indexed_range index( SinglePassRange& rng, typename range_difference::type index_value = 0) { BOOST_RANGE_CONCEPT_ASSERT(( boost::SinglePassRangeConcept )); return indexed_range( index_value, rng, typename range_traversal::type()); } template inline indexed_range index( const SinglePassRange& rng, typename range_difference::type index_value = 0) { BOOST_RANGE_CONCEPT_ASSERT(( boost::SinglePassRangeConcept )); return indexed_range( index_value, rng, typename range_traversal::type()); } } // namespace adaptors } // namespace boost #endif // include guard