diff --git a/doc/advance.rst b/doc/advance.rst new file mode 100644 index 0000000..34aa055 --- /dev/null +++ b/doc/advance.rst @@ -0,0 +1,75 @@ +.. Copyright (C) 2017 Michel Morin. + Distributed under 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) + +======= +advance +======= + +``boost::iterators::advance`` is an adapted version of ``std::advance`` for +the Boost iterator traversal concepts. + + +Header +------ + +```` + + +Synopsis +-------- + +:: + + template + constexpr void advance(Iterator& it, Distance n); + + +Description +----------- + +Moves ``it`` forward by ``n`` increments +(or backward by ``|n|`` decrements if ``n`` is negative). + + +Requirements +------------ + +``Iterator`` should model Incrementable Iterator. + + +Preconditions +------------- + +Let ``it``\ :sub:`i` be the iterator obtained by incrementing +(or decrementing if ``n`` is negative) ``it`` by *i*. All the iterators +``it``\ :sub:`i` for *i* = 0, 1, 2, ..., ``|n|`` should be valid. + +If ``Iterator`` does not model Bidirectional Traversal Iterator, +``n`` should be non-negative. + + +Complexity +---------- + +If ``Iterator`` models Random Access Traversal Iterator, it takes constant time; +otherwise it takes linear time. + + +Notes +----- + +- This function is not a customization point and is protected against + being found by argument-dependent lookup (ADL). +- This function is ``constexpr`` only in C++14 or later. + + +-------------------------------------------------------------------------------- + +| Author: Michel Morin +| Copyright |C| 2017 Michel Morin +| Distributed under the `Boost Software License, Version 1.0 + `_. + +.. |C| unicode:: U+00A9 .. COPYRIGHT SIGN diff --git a/doc/distance.rst b/doc/distance.rst new file mode 100644 index 0000000..d0697a9 --- /dev/null +++ b/doc/distance.rst @@ -0,0 +1,72 @@ +.. Copyright (C) 2017 Michel Morin. + Distributed under 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) + +======== +distance +======== + +``boost::iterators::distance`` is an adapted version of ``std::distance`` for +the Boost iterator traversal concepts. + + +Header +------ + +```` + + +Synopsis +-------- + +:: + + template + constexpr typename iterator_difference::type + distance(Iterator first, Iterator last); + + +Description +----------- + +Computes the (signed) distance from ``first`` to ``last``. + + +Requirements +------------ + +``Iterator`` should model Single Pass Iterator. + + +Preconditions +------------- + +If ``Iterator`` models Random Access Traversal Iterator, +``[first, last)`` or ``[last, first)`` should be valid; +otherwise ``[first, last)`` should be valid. + + +Complexity +---------- + +If ``Iterator`` models Random Access Traversal Iterator, it takes constant time; +otherwise it takes linear time. + + +Notes +----- + +- This function is not a customization point and is protected against + being found by argument-dependent lookup (ADL). +- This function is ``constexpr`` only in C++14 or later. + + +-------------------------------------------------------------------------------- + +| Author: Michel Morin +| Copyright |C| 2017 Michel Morin +| Distributed under the `Boost Software License, Version 1.0 + `_. + +.. |C| unicode:: U+00A9 .. COPYRIGHT SIGN diff --git a/doc/index.rst b/doc/index.rst index 924cfc8..7adab0b 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -213,6 +213,23 @@ __ zip_iterator.pdf Iterator Utilities ==================== +Operations +---------- + +The standard library does not handle new-style iterators properly, +because it knows nothing about the iterator traversal concepts. +The Boost.Iterator library provides implementations that fully understand +the new concepts for the two basic operations: + +- |advance|_ +- |distance|_ + +.. |advance| replace:: ``advance`` +.. _advance: advance.html + +.. |distance| replace:: ``distance`` +.. _distance: distance.html + Traits ------ diff --git a/doc/quickbook/algorithms.qbk b/doc/quickbook/algorithms.qbk new file mode 100644 index 0000000..e316c3a --- /dev/null +++ b/doc/quickbook/algorithms.qbk @@ -0,0 +1,63 @@ +[section:algorithms Algorithms] + +[section:next_prior Function templates `next()` and `prior()`] + +Certain data types, such as the C++ Standard Library's forward and bidirectional iterators, do not provide addition and subtraction via `operator+()` or `operator-()`. This means that non-modifying computation of the next or prior value requires a temporary, even though `operator++()` or `operator--()` is provided. It also means that writing code like `itr+1` inside a template restricts the iterator category to random access iterators. + +The `next()` and `prior()` functions provide a simple way around these problems: + + template + T next(T x) + { + return ++x; + } + + template + T next(T x, Distance n) + { + std::advance(x, n); + return x; + } + + template + T prior(T x) + { + return --x; + } + + template + T prior(T x, Distance n) + { + std::advance(x, -n); + return x; + } + +[note Function implementation above is given for exposition only. The actual implementation has the same effect for iterators, but has different properties, as documented later.] + +Usage is simple: + + const std::list::iterator p = get_some_iterator(); + const std::list::iterator prev = boost::prior(p); + const std::list::iterator next = boost::next(prev, 2); + +The distance from the given iterator should be supplied as an absolute value. For example, the iterator four iterators prior to the given iterator `p` may be obtained by `prior(p, 4)`. + +With C++11, the standard library provides `std::next()` and `std::prev()` function templates, which serve the same purpose. However, there are advantages to `boost::next()` and `boost::prior()`: + +- `boost::next()` and `boost::prior()` are compatible not only with iterators but with any type that provides arithmetic operators `operator++()`, `operator--()`, `operator+()`, `operator-()`, `operator+=()` or `operator-=()`. For example, this is possible: + + int x = 10; + int y = boost::next(x, 5); + assert(y == 15); + +- `boost::next()` and `boost::prior()` use [link iterator.concepts.concepts_traversal traversal categories] to select the most efficient implementation. For some kinds of iterators, such as [link iterator.specialized.transform transform iterators], the standard iterator category does not reflect the traversal category correctly and therefore `std::next()` and `std::prev()` will fall back to linear complexity. + +[section Acknowledgements] + +Contributed by [@http://www.boost.org/people/dave_abrahams.htm Dave Abrahams]. Two-argument versions by Daniel Walker. + +[endsect] + +[endsect] + +[endsect] diff --git a/doc/quickbook/iterator.qbk b/doc/quickbook/iterator.qbk index 0ec20b2..ff772de 100644 --- a/doc/quickbook/iterator.qbk +++ b/doc/quickbook/iterator.qbk @@ -176,6 +176,18 @@ templates. * _iterator_archetypes_: Concept archetype classes for the new iterators concepts. +[h2 Iterator Algorithms] + +The library provides a number of generic algorithms for use with iterators. These +algorithms take advantage of the new concepts defined by the library to provide +better performance and functionality. + +[def _next_prior_ [link iterator.algorithms.next_prior `next_prior.hpp`]] + +* _next_prior_: Provides `next()` and `prior()` functions for obtaining + next and prior iterators to a given iterator. The functions are also compatible + with non-iterator types. + [endsect] [include concepts.qbk] @@ -202,6 +214,8 @@ templates. [endsect] +[include algorithms.qbk] + [section:upgrading Upgrading from the old Boost Iterator Adaptor Library] [def _type_generator_ [@http://www.boost.org/more/generic_programming.html#type_generator type generator]] diff --git a/include/boost/next_prior.hpp b/include/boost/next_prior.hpp new file mode 100644 index 0000000..178cf67 --- /dev/null +++ b/include/boost/next_prior.hpp @@ -0,0 +1,189 @@ +// Boost next_prior.hpp header file ---------------------------------------// + +// (C) Copyright Dave Abrahams and Daniel Walker 1999-2003. +// Copyright (c) Andrey Semashev 2017 +// +// Distributed under 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) + +// See http://www.boost.org/libs/utility for documentation. + +// Revision History +// 13 Dec 2003 Added next(x, n) and prior(x, n) (Daniel Walker) + +#ifndef BOOST_NEXT_PRIOR_HPP_INCLUDED +#define BOOST_NEXT_PRIOR_HPP_INCLUDED + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { + +// Helper functions for classes like bidirectional iterators not supporting +// operator+ and operator- +// +// Usage: +// const std::list::iterator p = get_some_iterator(); +// const std::list::iterator prev = boost::prior(p); +// const std::list::iterator next = boost::next(prev, 2); + +// Contributed by Dave Abrahams + +namespace next_prior_detail { + +// The trait attempts to detect if the T type is an iterator. Class-type iterators are assumed +// to have the nested type iterator_category. Strictly speaking, this is not required to be the +// case (e.g. a user can specialize iterator_traits for T without defining T::iterator_category). +// Still, this is a good heuristic in practice, and we can't do anything better anyway. +// Since C++17 we can test for iterator_traits::iterator_category presence instead as it is +// required to be only present for iterators. +template< typename T, typename Void = void > +struct is_iterator +{ + static BOOST_CONSTEXPR_OR_CONST bool value = false; +}; + +template< typename T > +struct is_iterator< + T, + typename enable_if_has_type< +#if !defined(BOOST_NO_CXX17_ITERATOR_TRAITS) + typename std::iterator_traits< T >::iterator_category +#else + typename T::iterator_category +#endif + >::type +> +{ + static BOOST_CONSTEXPR_OR_CONST bool value = true; +}; + +template< typename T > +struct is_iterator< T*, void > +{ + static BOOST_CONSTEXPR_OR_CONST bool value = true; +}; + + +template< typename T, typename Distance, bool HasPlus = has_plus< T, Distance >::value > +struct next_plus_impl; + +template< typename T, typename Distance > +struct next_plus_impl< T, Distance, true > +{ + static T call(T x, Distance n) + { + return x + n; + } +}; + +template< typename T, typename Distance, bool HasPlusAssign = has_plus_assign< T, Distance >::value > +struct next_plus_assign_impl : + public next_plus_impl< T, Distance > +{ +}; + +template< typename T, typename Distance > +struct next_plus_assign_impl< T, Distance, true > +{ + static T call(T x, Distance n) + { + x += n; + return x; + } +}; + +template< typename T, typename Distance, bool IsIterator = is_iterator< T >::value > +struct next_advance_impl : + public next_plus_assign_impl< T, Distance > +{ +}; + +template< typename T, typename Distance > +struct next_advance_impl< T, Distance, true > +{ + static T call(T x, Distance n) + { + boost::iterators::advance(x, n); + return x; + } +}; + + +template< typename T, typename Distance, bool HasMinus = has_minus< T, Distance >::value > +struct prior_minus_impl; + +template< typename T, typename Distance > +struct prior_minus_impl< T, Distance, true > +{ + static T call(T x, Distance n) + { + return x - n; + } +}; + +template< typename T, typename Distance, bool HasMinusAssign = has_minus_assign< T, Distance >::value > +struct prior_minus_assign_impl : + public prior_minus_impl< T, Distance > +{ +}; + +template< typename T, typename Distance > +struct prior_minus_assign_impl< T, Distance, true > +{ + static T call(T x, Distance n) + { + x -= n; + return x; + } +}; + +template< typename T, typename Distance, bool IsIterator = is_iterator< T >::value > +struct prior_advance_impl : + public prior_minus_assign_impl< T, Distance > +{ +}; + +template< typename T, typename Distance > +struct prior_advance_impl< T, Distance, true > +{ + static T call(T x, Distance n) + { + // Avoid negating n to sidestep possible integer overflow + boost::iterators::reverse_iterator< T > rx(x); + boost::iterators::advance(rx, n); + return rx.base(); + } +}; + +} // namespace next_prior_detail + +template +inline T next(T x) { return ++x; } + +template +inline T next(T x, Distance n) +{ + return next_prior_detail::next_advance_impl< T, Distance >::call(x, n); +} + +template +inline T prior(T x) { return --x; } + +template +inline T prior(T x, Distance n) +{ + return next_prior_detail::prior_advance_impl< T, Distance >::call(x, n); +} + +} // namespace boost + +#endif // BOOST_NEXT_PRIOR_HPP_INCLUDED diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 927a98a..ac694e1 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -56,6 +56,7 @@ test-suite iterator [ run minimum_category.cpp ] [ compile-fail minimum_category_compile_fail.cpp ] + [ run next_prior_test.cpp ] [ run advance_test.cpp ] [ run distance_test.cpp ] ; diff --git a/test/next_prior_test.cpp b/test/next_prior_test.cpp new file mode 100644 index 0000000..734a212 --- /dev/null +++ b/test/next_prior_test.cpp @@ -0,0 +1,103 @@ +// Boost test program for next() and prior() utilities. + +// Copyright 2003 Daniel Walker. Use, modification, and distribution +// are subject to the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or a copy at +// http://www.boost.org/LICENSE_1_0.txt.) + +// See http://www.boost.org/libs/utility for documentation. + +// Revision History 13 Dec 2003 Initial Version (Daniel Walker) + +// next() and prior() are replacements for operator+ and operator- for +// non-random-access iterators. The semantics of these operators are +// such that after executing j = i + n, std::distance(i, j) equals +// n. Tests are provided to ensure next() has the same +// result. Parallel tests are provided for prior(). The tests call +// next() and prior() several times. next() and prior() are very +// simple functions, though, and it would be very strange if these +// tests were to fail. + +#include + +#include +#include + +#include + +template +bool plus_one_test(RandomAccessIterator first, RandomAccessIterator last, ForwardIterator first2) +{ + RandomAccessIterator i = first; + ForwardIterator j = first2; + while(i != last) + i = i + 1, j = boost::next(j); + return std::distance(first, i) == std::distance(first2, j); +} + +template +bool plus_n_test(RandomAccessIterator first, RandomAccessIterator last, ForwardIterator first2) +{ + RandomAccessIterator i = first; + ForwardIterator j = first2; + for(int n = 0; i != last; ++n) + i = first + n, j = boost::next(first2, n); + return std::distance(first, i) == std::distance(first2, j); +} + +template +bool minus_one_test(RandomAccessIterator first, RandomAccessIterator last, BidirectionalIterator last2) +{ + RandomAccessIterator i = last; + BidirectionalIterator j = last2; + while(i != first) + i = i - 1, j = boost::prior(j); + return std::distance(i, last) == std::distance(j, last2); +} + +template +bool minus_n_test(RandomAccessIterator first, RandomAccessIterator last, BidirectionalIterator last2) +{ + RandomAccessIterator i = last; + BidirectionalIterator j = last2; + for(int n = 0; i != first; ++n) + i = last - n, j = boost::prior(last2, n); + return std::distance(i, last) == std::distance(j, last2); +} + +template +bool minus_n_unsigned_test(Iterator first, Iterator last, Distance size) +{ + Iterator i = boost::prior(last, size); + return i == first; +} + +int main(int, char*[]) +{ + std::vector x(8); + std::list y(x.begin(), x.end()); + + // Tests with iterators + BOOST_TEST(plus_one_test(x.begin(), x.end(), y.begin())); + BOOST_TEST(plus_n_test(x.begin(), x.end(), y.begin())); + BOOST_TEST(minus_one_test(x.begin(), x.end(), y.end())); + BOOST_TEST(minus_n_test(x.begin(), x.end(), y.end())); + BOOST_TEST(minus_n_unsigned_test(x.begin(), x.end(), x.size())); + BOOST_TEST(minus_n_unsigned_test(y.begin(), y.end(), y.size())); + + BOOST_TEST(plus_one_test(x.rbegin(), x.rend(), y.begin())); + BOOST_TEST(plus_n_test(x.rbegin(), x.rend(), y.begin())); + BOOST_TEST(minus_one_test(x.rbegin(), x.rend(), y.end())); + BOOST_TEST(minus_n_test(x.rbegin(), x.rend(), y.end())); + BOOST_TEST(minus_n_unsigned_test(x.rbegin(), x.rend(), x.size())); + BOOST_TEST(minus_n_unsigned_test(x.rbegin(), x.rend(), y.size())); + + // Tests with integers + BOOST_TEST(boost::next(5) == 6); + BOOST_TEST(boost::next(5, 7) == 12); + BOOST_TEST(boost::prior(5) == 4); + BOOST_TEST(boost::prior(5, 7) == -2); + BOOST_TEST(boost::prior(5, 7u) == -2); + + return boost::report_errors(); +}