diff --git a/doc/reference/adaptors/indexed.qbk b/doc/reference/adaptors/indexed.qbk index d4ee91f..4503fe6 100644 --- a/doc/reference/adaptors/indexed.qbk +++ b/doc/reference/adaptors/indexed.qbk @@ -7,14 +7,60 @@ [table [[Syntax] [Code]] + [[Pipe] [`rng | boost::adaptors::indexed()`]] [[Pipe] [`rng | boost::adaptors::indexed(start_index)`]] + [[Function] [`boost::adaptors::index(rng)`]] [[Function] [`boost::adaptors::index(rng, start_index)`]] ] -* [*Returns:] A range adapted to return both the element and the associated index. The returned range consists of iterators that have in addition to the usual iterator member functions an `index()` member function that returns the appropriate index for the element in the sequence corresponding with the iterator. +[heading Description] +The index within each returned `boost::range::index_value` is equal to +`start_index` + the offset of the element from the beginning of the range. In +the versions of the functions that omit `start_index` the starting index is +taken to be `0`. + +* [*Purpose:] Adapt `rng` to return elements that have the corresponding value +from `rng` and a numeric index. +* [*Returns:] A range adapted to return both the element and the associated +index. The returned range has elements of type: + +`` +boost::range::index_value< + typename boost::range_reference::type, + typename boost::range_difference::type +> +`` + +The synopsis of index_value is as follows: +`` +template +class index_value : public boost::tuple +{ +public: + + typedef ...unspecified... index_type; + typedef ...unspecified... const_index_type; + + typedef ...unspecified... value_type; + typedef ...unspecified... const_value_type; + + // ...unspecified... constructors + + index_type index(); + const_index_type index() const; + + value_type value(); + const_value_type value() const; +}; +`` + * [*Range Category:] __single_pass_range__ * [*Range Return Type:] `boost::indexed_range` -* [*Returned Range Category:] The range category of `rng` +* [*Returned Range Category:] The range category of `rng` if and only if `rng` +is not a __bidirectional_range__. If `rng` is a __bidirectional_range__ then the +returned range category is __forward_range__. The rationale for the demotion of +__bidirectional_range__ inputs to __forward_range__ is to avoid slow calculation +of indices for `boost::end(rng)`. [section:indexed_example indexed example] [import ../../../test/adaptor_test/indexed_example.cpp] diff --git a/include/boost/range/adaptor/indexed.hpp b/include/boost/range/adaptor/indexed.hpp index cafedf1..a426bd6 100644 --- a/include/boost/range/adaptor/indexed.hpp +++ b/include/boost/range/adaptor/indexed.hpp @@ -1,172 +1,370 @@ -// Boost.Range library +// Copyright 2014 Neil Groves // -// Copyright Thorsten Ottosen, Neil Groves 2006 - 2008. Use, modification and -// distribution is subject to the Boost Software License, Version -// 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// 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) // -// For more information, see http://www.boost.org/libs/range/ +// 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 -#ifndef BOOST_RANGE_ADAPTOR_INDEXED_IMPL_HPP -#define BOOST_RANGE_ADAPTOR_INDEXED_IMPL_HPP - -#include -#ifdef BOOST_MSVC -#pragma warning( push ) -#pragma warning( disable : 4355 ) -#endif - +#include #include #include +#include +#include #include #include -#include -#include +#include +#include +#include +#include +#include namespace boost { namespace adaptors { - // This structure exists to carry the parameters from the '|' operator - // to the index adapter. The expression rng | indexed(1) instantiates - // this structure and passes it as the right-hand operand to the - // '|' operator. - struct indexed - { - explicit indexed(std::size_t x) : val(x) {} - std::size_t val; - }; + +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() + { } - namespace range_detail + index_value(typename tuples::access_traits::parameter_type t0, + typename tuples::access_traits::parameter_type t1) + : base_t(t0, t1) { - template< class Iter > - class indexed_iterator - : public boost::iterator_adaptor< indexed_iterator, Iter > - { - private: - typedef boost::iterator_adaptor< indexed_iterator, Iter > - base; + } - typedef BOOST_DEDUCED_TYPENAME base::difference_type index_type; + // member functions index(), value() (non-const and const) + index_type index() + { + return boost::tuples::get<0>(*this); + } - index_type m_index; + const_index_type index() const + { + return boost::tuples::get<0>(*this); + } - public: - indexed_iterator() - : m_index(index_type()) {} - - explicit indexed_iterator( Iter i, index_type index ) - : base(i), m_index(index) - { - BOOST_ASSERT( m_index >= 0 && "Indexed Iterator out of bounds" ); - } + value_type value() + { + return boost::tuples::get<1>(*this); + } - index_type index() const - { - return m_index; - } + const_value_type value() const + { + return boost::tuples::get<1>(*this); + } +}; - private: - friend class boost::iterator_core_access; + } // namespace range - void increment() - { - ++m_index; - ++(this->base_reference()); - } +namespace range_detail +{ +template +struct indexed_iterator_value_type +{ + typedef ::boost::range::index_value< + typename iterator_reference::type, + typename iterator_difference::type + > type; +}; - void decrement() - { - BOOST_ASSERT( m_index > 0 && "Indexed Iterator out of bounds" ); - --m_index; - --(this->base_reference()); - } +// 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; - void advance( index_type n ) - { - m_index += n; - BOOST_ASSERT( m_index >= 0 && "Indexed Iterator out of bounds" ); - this->base_reference() += n; - } - }; +public: - template< class Rng > - struct indexed_range : - iterator_range< indexed_iterator::type> > - { - private: - typedef indexed_iterator::type> - iter_type; - typedef iterator_range - base; - public: - template< class Index > - indexed_range( Index i, Rng& r ) - : base( iter_type(boost::begin(r), i), iter_type(boost::end(r),i) ) - { } - }; + typedef typename mpl::if_< + is_convertible, + random_access_traversal_tag, + typename mpl::if_< + is_convertible, + forward_traversal_tag, + wrapped_traversal + >::type + >::type type; +}; - } // 'range_detail' +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 - // Make this available to users of this library. It will sometimes be - // required since it is the return type of operator '|' and - // index(). using range_detail::indexed_range; namespace adaptors { - template< class SinglePassRange > - inline indexed_range - operator|( SinglePassRange& r, - const indexed& f ) - { - BOOST_RANGE_CONCEPT_ASSERT(( - SinglePassRangeConcept)); - - return indexed_range( f.val, r ); - } - - template< class SinglePassRange > - inline indexed_range - operator|( const SinglePassRange& r, - const indexed& f ) - { - BOOST_RANGE_CONCEPT_ASSERT(( - SinglePassRangeConcept)); - - return indexed_range( f.val, r ); - } - - template - inline indexed_range - index(SinglePassRange& rng, Index index_value) - { - BOOST_RANGE_CONCEPT_ASSERT(( - SinglePassRangeConcept)); - - return indexed_range(index_value, rng); - } - - template - inline indexed_range - index(const SinglePassRange& rng, Index index_value) - { - BOOST_RANGE_CONCEPT_ASSERT(( - SinglePassRangeConcept)); - - return indexed_range(index_value, rng); - } - } // '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()); } -#ifdef BOOST_MSVC -#pragma warning( pop ) -#endif +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()); +} -#endif +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 diff --git a/include/boost/range/traversal.hpp b/include/boost/range/traversal.hpp new file mode 100644 index 0000000..237b3e8 --- /dev/null +++ b/include/boost/range/traversal.hpp @@ -0,0 +1,31 @@ +// Boost.Range library +// +// Copyright Neil Groves 2014. 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) +// +// For more information, see http://www.boost.org/libs/range/ +// + +#ifndef BOOST_RANGE_TRAVERSAL_HPP +#define BOOST_RANGE_TRAVERSAL_HPP + +#if defined(_MSC_VER) +# pragma once +#endif + +#include +#include +#include + +namespace boost +{ + template + struct range_traversal + : iterator_traversal::type> + { + }; +} + +#endif diff --git a/test/adaptor_test/indexed.cpp b/test/adaptor_test/indexed.cpp index a5bef4d..f4085be 100644 --- a/test/adaptor_test/indexed.cpp +++ b/test/adaptor_test/indexed.cpp @@ -1,6 +1,6 @@ // Boost.Range library // -// Copyright Neil Groves 2009. Use, modification and +// Copyright Neil Groves 2014. 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) @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -23,83 +24,129 @@ #include "../test_utils.hpp" -namespace boost +namespace boost_range_test { namespace { - template< class Container > - void indexed_test_impl( Container& c ) - { - using namespace boost::adaptors; - typedef BOOST_DEDUCED_TYPENAME Container::value_type value_t; +template +void check_result( + const Container& reference_range, + const AdaptedRange& adapted_range, + std::ptrdiff_t start_index + ) +{ + typedef typename boost::range_iterator::type + reference_iterator; - // This is my preferred syntax using the | operator. - std::vector< value_t > test_result1; - boost::push_back(test_result1, c | indexed(0)); + typedef typename boost::range_iterator::type + adapted_iterator; - // This is an alternative syntax preferred by some. - std::vector< value_t > test_result2; - boost::push_back(test_result2, adaptors::index(c, 0)); + BOOST_REQUIRE_EQUAL(boost::size(reference_range), + boost::size(adapted_range)); - BOOST_CHECK_EQUAL_COLLECTIONS( c.begin(), c.end(), - test_result1.begin(), test_result1.end() ); - - BOOST_CHECK_EQUAL_COLLECTIONS( c.begin(), c.end(), - test_result2.begin(), test_result2.end() ); - - boost::indexed_range< Container > test_result3 - = c | indexed(0); - - typedef BOOST_DEDUCED_TYPENAME boost::range_const_iterator< - boost::indexed_range< Container > >::type iter_t; - - iter_t it = test_result3.begin(); - for (std::size_t i = 0, count = c.size(); i < count; ++i) - { - BOOST_CHECK_EQUAL( i, static_cast(it.index()) ); - ++it; - } - - } - - template< class Container > - void indexed_test_impl() - { - using namespace boost::assign; - - Container c; - - // test empty container - indexed_test_impl(c); - - // test one element - c += 1; - indexed_test_impl(c); - - // test many elements - c += 1,2,2,2,3,4,4,4,4,5,6,7,8,9,9; - indexed_test_impl(c); - } - - void indexed_test() - { - indexed_test_impl< std::vector< int > >(); - indexed_test_impl< std::list< int > >(); - - check_random_access_range_concept(std::vector() | boost::adaptors::indexed(0)); - check_bidirectional_range_concept(std::list() | boost::adaptors::indexed(0)); - } + reference_iterator reference_it = boost::begin(reference_range); + adapted_iterator adapted_it = boost::begin(adapted_range); + for (std::ptrdiff_t i = start_index; + reference_it != boost::end(reference_range); + ++reference_it, ++adapted_it, ++i) + { + BOOST_CHECK_EQUAL(i, adapted_it->index()); + BOOST_CHECK_EQUAL(*reference_it, adapted_it->value()); } } +template +void indexed_test_impl(Container& c, std::ptrdiff_t start_index) +{ + // This is my preferred syntax using the | operator. + check_result(c, c | boost::adaptors::indexed(), 0); + check_result(c, c | boost::adaptors::indexed(start_index), start_index); + + // This is the function syntax + check_result(c, boost::adaptors::index(c), 0); + check_result(c, boost::adaptors::index(c, start_index), start_index); +} + +template +void indexed_test_impl(Container& c) +{ + indexed_test_impl(c, 0); + indexed_test_impl(c, -1); + indexed_test_impl(c, 4); +} + +template +void indexed_test_impl() +{ + using namespace boost::assign; + + Container c; + + // test empty container + indexed_test_impl(c); + + // test one element + c += 1; + indexed_test_impl(c); + + // test many elements + c += 1,2,2,2,3,4,4,4,4,5,6,7,8,9,9; + indexed_test_impl(c); +} + +template +void check_traversal(const Range& rng) +{ + BOOST_STATIC_ASSERT( + boost::is_convertible< + typename boost::range_traversal::type, + Traversal + >::value); +} + +template +void check_not_traversal(const Range& rng) +{ + BOOST_STATIC_ASSERT( + !boost::is_convertible< + typename boost::range_traversal::type, + Traversal + >::value); +} + +void indexed_test() +{ + indexed_test_impl< std::vector< int > >(); + indexed_test_impl< std::list< int > >(); + + std::vector vi; + + check_traversal( + vi | boost::adaptors::indexed()); + + std::list li; + + check_traversal( + li | boost::adaptors::indexed()); + + check_not_traversal( + li | boost::adaptors::indexed()); + + check_not_traversal( + li | boost::adaptors::indexed()); +} + + } // anonymous namesapce +} // namespace boost_range_test + boost::unit_test::test_suite* -init_unit_test_suite(int argc, char* argv[]) +init_unit_test_suite(int, char*[]) { boost::unit_test::test_suite* test - = BOOST_TEST_SUITE( "RangeTestSuite.adaptor.indexed" ); + = BOOST_TEST_SUITE( "Boost.Range indexed adaptor test suite" ); - test->add( BOOST_TEST_CASE( &boost::indexed_test ) ); + test->add(BOOST_TEST_CASE(&boost_range_test::indexed_test)); return test; } diff --git a/test/adaptor_test/indexed_example.cpp b/test/adaptor_test/indexed_example.cpp index 1bcbf9c..c65e62e 100644 --- a/test/adaptor_test/indexed_example.cpp +++ b/test/adaptor_test/indexed_example.cpp @@ -9,8 +9,12 @@ // For more information, see http://www.boost.org/libs/range/ // //[indexed_example + +//<- +#include +//-> + #include -#include #include #include #include @@ -20,8 +24,6 @@ #include #include -#include - namespace { @@ -35,14 +37,14 @@ void check_element_and_index( BOOST_CHECK_EQUAL( std::distance(test_first, test_last), std::distance(reference_first, reference_last) ); - int reference_index = 0; + std::ptrdiff_t reference_index = 0; Iterator1 test_it = test_first; Iterator2 reference_it = reference_first; for (; test_it != test_last; ++test_it, ++reference_it, ++reference_index) { - BOOST_CHECK_EQUAL( *test_it, *reference_it ); - BOOST_CHECK_EQUAL( test_it.index(), reference_index ); + BOOST_CHECK_EQUAL(test_it->value(), *reference_it); + BOOST_CHECK_EQUAL(test_it->index(), reference_index); } } @@ -51,24 +53,11 @@ void check_element_and_index( const SinglePassRange1& test_rng, const SinglePassRange2& reference_rng) { - check_element_and_index(boost::begin(test_rng), boost::end(test_rng), + check_element_and_index( + boost::begin(test_rng), boost::end(test_rng), boost::begin(reference_rng), boost::end(reference_rng)); } -//-> -template -void display_element_and_index(Iterator first, Iterator last) -{ - for (Iterator it = first; it != last; ++it) - { - std::cout << "Element = " << *it << " Index = " << it.index() << std::endl; - } -} - -template -void display_element_and_index(const SinglePassRange& rng) -{ - display_element_and_index(boost::begin(rng), boost::end(rng)); -} +//-> //<- void indexed_example_test() @@ -81,8 +70,19 @@ void indexed_example_test() std::vector input; input += 10,20,30,40,50,60,70,80,90; - display_element_and_index( input | indexed(0) ); - +//<- +#ifndef BOOST_NO_CXX11_RANGE_BASED_FOR +//-> + for (const auto& element : input | indexed(0)) + { + std::cout << "Element = " << element.value() + << " Index = " << element.index() + << std::endl; + } +//<- +#endif // C++11 has range for loop +//-> + //= return 0; //=} //]