From e2c927628c64f4267a8c0f26255a5e6cd36a2a3e Mon Sep 17 00:00:00 2001 From: morinmorin Date: Sun, 23 Jul 2017 23:46:26 +0900 Subject: [PATCH 01/15] Add documentation for advance and distance --- doc/advance.rst | 75 ++++++++++++++++++++++++++++++++++++++++++++++++ doc/distance.rst | 72 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 147 insertions(+) create mode 100644 doc/advance.rst create mode 100644 doc/distance.rst 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 From bfcf52ace609ceae11ccc9c6fb428f81b23fca9f Mon Sep 17 00:00:00 2001 From: morinmorin Date: Sun, 23 Jul 2017 23:49:30 +0900 Subject: [PATCH 02/15] Add links to advance and distance --- doc/index.rst | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) 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 ------ From 5ba5f2c2d46ef9aa782f80b45fcca52cb61e29bf Mon Sep 17 00:00:00 2001 From: Aleksey Gurtovoy Date: Mon, 17 Feb 2003 06:20:57 +0000 Subject: [PATCH 03/15] split utility.hpp header [SVN r17472] --- include/boost/next_prior.hpp | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 include/boost/next_prior.hpp diff --git a/include/boost/next_prior.hpp b/include/boost/next_prior.hpp new file mode 100644 index 0000000..d236766 --- /dev/null +++ b/include/boost/next_prior.hpp @@ -0,0 +1,33 @@ +// Boost next_prior.hpp header file ---------------------------------------// + +// (C) Copyright Boost.org 1999-2003. Permission to copy, use, modify, sell +// and distribute this software is granted provided this copyright +// notice appears in all copies. This software is provided "as is" without +// express or implied warranty, and with no claim as to its suitability for +// any purpose. + +// See http://www.boost.org/libs/utility for documentation. + +#ifndef BOOST_NEXT_PRIOR_HPP_INCLUDED +#define BOOST_NEXT_PRIOR_HPP_INCLUDED + +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); + +// Contributed by Dave Abrahams + +template +inline T next(T x) { return ++x; } + +template +inline T prior(T x) { return --x; } + +} // namespace boost + +#endif // BOOST_NEXT_PRIOR_HPP_INCLUDED From a4bacb5077662b04525e11d5ee67f970375046f4 Mon Sep 17 00:00:00 2001 From: Dave Abrahams Date: Tue, 23 Dec 2003 14:59:59 +0000 Subject: [PATCH 04/15] Extended next/prior using patch from Daniel Walker (Daniel.Walker-at-bowneglobal.com) [SVN r21382] --- include/boost/next_prior.hpp | 20 +++++++++ test/next_prior_test.cpp | 79 ++++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+) create mode 100755 test/next_prior_test.cpp diff --git a/include/boost/next_prior.hpp b/include/boost/next_prior.hpp index d236766..3afeb78 100644 --- a/include/boost/next_prior.hpp +++ b/include/boost/next_prior.hpp @@ -8,9 +8,14 @@ // 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 + namespace boost { // Helper functions for classes like bidirectional iterators not supporting @@ -19,15 +24,30 @@ namespace boost { // 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 template inline T next(T x) { return ++x; } +template +inline T next(T x, Distance n) +{ + std::advance(x, n); + return x; +} + template inline T prior(T x) { return --x; } +template +inline T prior(T x, Distance n) +{ + std::advance(x, -n); + return x; +} + } // namespace boost #endif // BOOST_NEXT_PRIOR_HPP_INCLUDED diff --git a/test/next_prior_test.cpp b/test/next_prior_test.cpp new file mode 100755 index 0000000..ebce472 --- /dev/null +++ b/test/next_prior_test.cpp @@ -0,0 +1,79 @@ +// 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. + +#define BOOST_INCLUDE_MAIN +#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); +} + +int test_main(int, char*[]) +{ + std::vector x(8); + std::list y(x.begin(), x.end()); + + BOOST_REQUIRE(plus_one_test(x.begin(), x.end(), y.begin())); + BOOST_REQUIRE(plus_n_test(x.begin(), x.end(), y.begin())); + BOOST_REQUIRE(minus_one_test(x.begin(), x.end(), y.end())); + BOOST_REQUIRE(minus_n_test(x.begin(), x.end(), y.end())); + return 0; +} From 398819237e9a8840c50fe47a02e1dee4f473ec1e Mon Sep 17 00:00:00 2001 From: John Maddock Date: Tue, 10 Aug 2004 12:53:34 +0000 Subject: [PATCH 05/15] Removed Boost.org copyrights and replaced with originating authors copyright instead. [SVN r24372] --- include/boost/next_prior.hpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/include/boost/next_prior.hpp b/include/boost/next_prior.hpp index 3afeb78..d205f35 100644 --- a/include/boost/next_prior.hpp +++ b/include/boost/next_prior.hpp @@ -1,10 +1,8 @@ // Boost next_prior.hpp header file ---------------------------------------// -// (C) Copyright Boost.org 1999-2003. Permission to copy, use, modify, sell -// and distribute this software is granted provided this copyright -// notice appears in all copies. This software is provided "as is" without -// express or implied warranty, and with no claim as to its suitability for -// any purpose. +// (C) Copyright Aleksey Gurtovoy 1999-2003. 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. From abe77db3e00b8f7c6e2a89aef5aea68dfddbc5b0 Mon Sep 17 00:00:00 2001 From: John Maddock Date: Sun, 15 Aug 2004 10:13:49 +0000 Subject: [PATCH 06/15] Added Daryle Walker's copyrights as requested. [SVN r24481] --- include/boost/next_prior.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/next_prior.hpp b/include/boost/next_prior.hpp index d205f35..e1d2e42 100644 --- a/include/boost/next_prior.hpp +++ b/include/boost/next_prior.hpp @@ -1,6 +1,6 @@ // Boost next_prior.hpp header file ---------------------------------------// -// (C) Copyright Aleksey Gurtovoy 1999-2003. Distributed under the Boost +// (C) Copyright Dave Abrahams and Daniel Walker 1999-2003. 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) From d1b22ac8e8ad8c369e76b82cfd48170049480534 Mon Sep 17 00:00:00 2001 From: Andrey Semashev Date: Thu, 12 Jun 2014 01:44:35 +0400 Subject: [PATCH 07/15] Remove executable bit from the file. --- test/next_prior_test.cpp | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 test/next_prior_test.cpp diff --git a/test/next_prior_test.cpp b/test/next_prior_test.cpp old mode 100755 new mode 100644 From 9d054b25ce39f6104a9fae317db5be9eadfaa100 Mon Sep 17 00:00:00 2001 From: Andrey Semashev Date: Tue, 24 Jun 2014 01:05:32 +0400 Subject: [PATCH 08/15] Reworked next() and prior() taking the distance arguments. The new version should provide the expected behavior in the case (prior(v.end(), v.size()) == v.begin()). It should also work with integers now, as was originally intended by David Abrahams. Added tests to verify these new use cases. --- include/boost/next_prior.hpp | 122 +++++++++++++++++++++++++++++++++-- test/next_prior_test.cpp | 18 ++++++ 2 files changed, 136 insertions(+), 4 deletions(-) diff --git a/include/boost/next_prior.hpp b/include/boost/next_prior.hpp index e1d2e42..7854ec4 100644 --- a/include/boost/next_prior.hpp +++ b/include/boost/next_prior.hpp @@ -13,6 +13,17 @@ #define BOOST_NEXT_PRIOR_HPP_INCLUDED #include +#if defined(_MSC_VER) && _MSC_VER <= 1310 +#include +#include +#endif +#include +#include +#include +#include +#include +#include +#include namespace boost { @@ -26,14 +37,118 @@ namespace boost { // Contributed by Dave Abrahams +namespace next_prior_detail { + +template< typename T, typename Distance, bool HasPlus = has_plus< T, Distance >::value > +struct next_impl2 +{ + static T call(T x, Distance n) + { + std::advance(x, n); + return x; + } +}; + +template< typename T, typename Distance > +struct next_impl2< 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_impl1 : + public next_impl2< T, Distance > +{ +}; + +template< typename T, typename Distance > +struct next_impl1< T, Distance, true > +{ + static T call(T x, Distance n) + { + x += n; + return x; + } +}; + + +template< + typename T, + typename Distance, + typename PromotedDistance = typename integral_promotion< Distance >::type, +#if !defined(_MSC_VER) || _MSC_VER > 1310 + bool IsUInt = is_unsigned< PromotedDistance >::value +#else + // MSVC 7.1 has problems with applying is_unsigned to non-integral types + bool IsUInt = mpl::and_< is_integral< PromotedDistance >, is_unsigned< PromotedDistance > >::value +#endif +> +struct prior_impl3 +{ + static T call(T x, Distance n) + { + std::advance(x, -n); + return x; + } +}; + +template< typename T, typename Distance, typename PromotedDistance > +struct prior_impl3< T, Distance, PromotedDistance, true > +{ + static T call(T x, Distance n) + { + typedef typename make_signed< PromotedDistance >::type signed_distance; + std::advance(x, -static_cast< signed_distance >(static_cast< PromotedDistance >(n))); + return x; + } +}; + + +template< typename T, typename Distance, bool HasMinus = has_minus< T, Distance >::value > +struct prior_impl2 : + public prior_impl3< T, Distance > +{ +}; + +template< typename T, typename Distance > +struct prior_impl2< 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_impl1 : + public prior_impl2< T, Distance > +{ +}; + +template< typename T, typename Distance > +struct prior_impl1< T, Distance, true > +{ + static T call(T x, Distance n) + { + x -= n; + return x; + } +}; + +} // namespace next_prior_detail + template inline T next(T x) { return ++x; } template inline T next(T x, Distance n) { - std::advance(x, n); - return x; + return next_prior_detail::next_impl1< T, Distance >::call(x, n); } template @@ -42,8 +157,7 @@ inline T prior(T x) { return --x; } template inline T prior(T x, Distance n) { - std::advance(x, -n); - return x; + return next_prior_detail::prior_impl1< T, Distance >::call(x, n); } } // namespace boost diff --git a/test/next_prior_test.cpp b/test/next_prior_test.cpp index ebce472..4f9b01a 100644 --- a/test/next_prior_test.cpp +++ b/test/next_prior_test.cpp @@ -66,14 +66,32 @@ bool minus_n_test(RandomAccessIterator first, RandomAccessIterator last, Bidirec 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 test_main(int, char*[]) { std::vector x(8); std::list y(x.begin(), x.end()); + // Tests with iterators BOOST_REQUIRE(plus_one_test(x.begin(), x.end(), y.begin())); BOOST_REQUIRE(plus_n_test(x.begin(), x.end(), y.begin())); BOOST_REQUIRE(minus_one_test(x.begin(), x.end(), y.end())); BOOST_REQUIRE(minus_n_test(x.begin(), x.end(), y.end())); + BOOST_REQUIRE(minus_n_unsigned_test(x.begin(), x.end(), x.size())); + BOOST_REQUIRE(minus_n_unsigned_test(y.begin(), y.end(), y.size())); + + // Tests with integers + BOOST_REQUIRE(boost::next(5) == 6); + BOOST_REQUIRE(boost::next(5, 7) == 12); + BOOST_REQUIRE(boost::prior(5) == 4); + BOOST_REQUIRE(boost::prior(5, 7) == -2); + BOOST_REQUIRE(boost::prior(5, 7u) == -2); + return 0; } From d251a6f5154cdda5bb1622923220e7b3428bebf5 Mon Sep 17 00:00:00 2001 From: Andrey Semashev Date: Sun, 9 Jul 2017 02:27:47 +0300 Subject: [PATCH 09/15] Reworked iterator handling in next/prior helpers. The new implementation tries to detect if the incremented/decremented type is an iterator first and if not falls back to operator probing. This way iterators that are not SFINAE-friendly (i.e. unconditionally define arithmetic operators regardless of the iterator category) are still treated as iterators through std::advance and do not fail the compilation. The iterator detection is based on probing for the nested iterator_category type that is expected to be present in class-type iterators. This heuristic is not flawless since iterators are not required to defined this type. User-defined iterators may not have it and instead specialize std::iterator_traits. This use case is not covered by the current implementation and will likely fail to compile. With C++17 SFINAE-friendly std::iterator_traits this can be fixed, but currently Boost.Config lacks the macro to detect availability of this feature. Support for it can be added by a later commit. Also simplified boost::prior for iterators, removing the possibility of integer overflow caused by negation of the distance value. --- include/boost/next_prior.hpp | 126 ++++++++++++++++++++--------------- 1 file changed, 71 insertions(+), 55 deletions(-) diff --git a/include/boost/next_prior.hpp b/include/boost/next_prior.hpp index 7854ec4..986c9d9 100644 --- a/include/boost/next_prior.hpp +++ b/include/boost/next_prior.hpp @@ -1,8 +1,11 @@ // Boost next_prior.hpp header file ---------------------------------------// -// (C) Copyright Dave Abrahams and Daniel Walker 1999-2003. 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) +// (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. @@ -13,13 +16,7 @@ #define BOOST_NEXT_PRIOR_HPP_INCLUDED #include -#if defined(_MSC_VER) && _MSC_VER <= 1310 -#include -#include -#endif -#include -#include -#include +#include #include #include #include @@ -39,18 +36,40 @@ namespace boost { namespace next_prior_detail { -template< typename T, typename Distance, bool HasPlus = has_plus< T, Distance >::value > -struct next_impl2 +// 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 > +struct is_iterator { - static T call(T x, Distance n) - { - std::advance(x, n); - return x; - } +private: + typedef char yes_type; + typedef char (&no_type)[2]; + + template< typename U > + static yes_type check_iterator_category(typename U::iterator_category*); + template< typename U > + static no_type check_iterator_category(...); + +public: + static BOOST_CONSTEXPR_OR_CONST bool value = sizeof(is_iterator< T >::BOOST_NESTED_TEMPLATE check_iterator_category< T >(0)) == sizeof(yes_type); }; +template< typename T > +struct is_iterator< T* > +{ + 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_impl2< T, Distance, true > +struct next_plus_impl< T, Distance, true > { static T call(T x, Distance n) { @@ -58,15 +77,14 @@ struct next_impl2< T, Distance, true > } }; - template< typename T, typename Distance, bool HasPlusAssign = has_plus_assign< T, Distance >::value > -struct next_impl1 : - public next_impl2< T, Distance > +struct next_plus_assign_impl : + public next_plus_impl< T, Distance > { }; template< typename T, typename Distance > -struct next_impl1< T, Distance, true > +struct next_plus_assign_impl< T, Distance, true > { static T call(T x, Distance n) { @@ -75,47 +93,28 @@ struct next_impl1< T, Distance, true > } }; - -template< - typename T, - typename Distance, - typename PromotedDistance = typename integral_promotion< Distance >::type, -#if !defined(_MSC_VER) || _MSC_VER > 1310 - bool IsUInt = is_unsigned< PromotedDistance >::value -#else - // MSVC 7.1 has problems with applying is_unsigned to non-integral types - bool IsUInt = mpl::and_< is_integral< PromotedDistance >, is_unsigned< PromotedDistance > >::value -#endif -> -struct prior_impl3 +template< typename T, typename Distance, bool IsIterator = is_iterator< T >::value > +struct next_advance_impl : + public next_plus_assign_impl< T, Distance > { - static T call(T x, Distance n) - { - std::advance(x, -n); - return x; - } }; -template< typename T, typename Distance, typename PromotedDistance > -struct prior_impl3< T, Distance, PromotedDistance, true > +template< typename T, typename Distance > +struct next_advance_impl< T, Distance, true > { static T call(T x, Distance n) { - typedef typename make_signed< PromotedDistance >::type signed_distance; - std::advance(x, -static_cast< signed_distance >(static_cast< PromotedDistance >(n))); + std::advance(x, n); return x; } }; template< typename T, typename Distance, bool HasMinus = has_minus< T, Distance >::value > -struct prior_impl2 : - public prior_impl3< T, Distance > -{ -}; +struct prior_minus_impl; template< typename T, typename Distance > -struct prior_impl2< T, Distance, true > +struct prior_minus_impl< T, Distance, true > { static T call(T x, Distance n) { @@ -123,15 +122,14 @@ struct prior_impl2< T, Distance, true > } }; - template< typename T, typename Distance, bool HasMinusAssign = has_minus_assign< T, Distance >::value > -struct prior_impl1 : - public prior_impl2< T, Distance > +struct prior_minus_assign_impl : + public prior_minus_impl< T, Distance > { }; template< typename T, typename Distance > -struct prior_impl1< T, Distance, true > +struct prior_minus_assign_impl< T, Distance, true > { static T call(T x, Distance n) { @@ -140,6 +138,24 @@ struct prior_impl1< T, Distance, true > } }; +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 + std::reverse_iterator< T > rx(x); + std::advance(rx, n); + return rx.base(); + } +}; + } // namespace next_prior_detail template @@ -148,7 +164,7 @@ inline T next(T x) { return ++x; } template inline T next(T x, Distance n) { - return next_prior_detail::next_impl1< T, Distance >::call(x, n); + return next_prior_detail::next_advance_impl< T, Distance >::call(x, n); } template @@ -157,7 +173,7 @@ inline T prior(T x) { return --x; } template inline T prior(T x, Distance n) { - return next_prior_detail::prior_impl1< T, Distance >::call(x, n); + return next_prior_detail::prior_advance_impl< T, Distance >::call(x, n); } } // namespace boost From bab02bb82e959d2c646fb5120f29a058cc1496bf Mon Sep 17 00:00:00 2001 From: Brian Minard Date: Sat, 8 Jul 2017 09:15:33 -0400 Subject: [PATCH 10/15] Add test cases for std::reverse_iterator --- test/next_prior_test.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/next_prior_test.cpp b/test/next_prior_test.cpp index 4f9b01a..cd2812f 100644 --- a/test/next_prior_test.cpp +++ b/test/next_prior_test.cpp @@ -86,6 +86,13 @@ int test_main(int, char*[]) BOOST_REQUIRE(minus_n_unsigned_test(x.begin(), x.end(), x.size())); BOOST_REQUIRE(minus_n_unsigned_test(y.begin(), y.end(), y.size())); + BOOST_REQUIRE(plus_one_test(x.rbegin(), x.rend(), y.begin())); + BOOST_REQUIRE(plus_n_test(x.rbegin(), x.rend(), y.begin())); + BOOST_REQUIRE(minus_one_test(x.rbegin(), x.rend(), y.end())); + BOOST_REQUIRE(minus_n_test(x.rbegin(), x.rend(), y.end())); + BOOST_REQUIRE(minus_n_unsigned_test(x.rbegin(), x.rend(), x.size())); + BOOST_REQUIRE(minus_n_unsigned_test(x.rbegin(), x.rend(), y.size())); + // Tests with integers BOOST_REQUIRE(boost::next(5) == 6); BOOST_REQUIRE(boost::next(5, 7) == 12); From 5ba36b063e159171973c53fa1bf9af1d40a24607 Mon Sep 17 00:00:00 2001 From: Andrey Semashev Date: Wed, 12 Jul 2017 20:14:48 +0300 Subject: [PATCH 11/15] Changed iterator_category nested type detection to work with MSVC and different versions of gcc. --- include/boost/next_prior.hpp | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/include/boost/next_prior.hpp b/include/boost/next_prior.hpp index 986c9d9..58da590 100644 --- a/include/boost/next_prior.hpp +++ b/include/boost/next_prior.hpp @@ -17,6 +17,7 @@ #include #include +#include #include #include #include @@ -42,24 +43,20 @@ namespace next_prior_detail { // 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 > +template< typename T, typename Void = void > struct is_iterator { -private: - typedef char yes_type; - typedef char (&no_type)[2]; - - template< typename U > - static yes_type check_iterator_category(typename U::iterator_category*); - template< typename U > - static no_type check_iterator_category(...); - -public: - static BOOST_CONSTEXPR_OR_CONST bool value = sizeof(is_iterator< T >::BOOST_NESTED_TEMPLATE check_iterator_category< T >(0)) == sizeof(yes_type); + static BOOST_CONSTEXPR_OR_CONST bool value = false; }; template< typename T > -struct is_iterator< T* > +struct is_iterator< T, typename enable_if_has_type< typename T::iterator_category >::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; }; From e3577e76875545375e7aa15fbfc2b2a664cd3004 Mon Sep 17 00:00:00 2001 From: Andrey Semashev Date: Wed, 12 Jul 2017 21:08:13 +0300 Subject: [PATCH 12/15] Use Boost.Iterator to advance iterators. By using Boost.Iterator we rely on the separate traversal category instead of the standard iterator category to advance iterators efficiently. For instance, this allows to advance transform iterators over a random access sequence in constant time, despite that they are formally input iterators. Also, std::reverse_iterator formally requires at least bidirectional iterator as the underlying iterator type. Transform iterators from the example above don't qualify, so potentially std::reverse_iterator could fail to compile. --- include/boost/next_prior.hpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/include/boost/next_prior.hpp b/include/boost/next_prior.hpp index 58da590..bad46ed 100644 --- a/include/boost/next_prior.hpp +++ b/include/boost/next_prior.hpp @@ -22,6 +22,8 @@ #include #include #include +#include +#include namespace boost { @@ -101,7 +103,7 @@ struct next_advance_impl< T, Distance, true > { static T call(T x, Distance n) { - std::advance(x, n); + boost::iterators::advance(x, n); return x; } }; @@ -147,8 +149,8 @@ struct prior_advance_impl< T, Distance, true > static T call(T x, Distance n) { // Avoid negating n to sidestep possible integer overflow - std::reverse_iterator< T > rx(x); - std::advance(rx, n); + boost::iterators::reverse_iterator< T > rx(x); + boost::iterators::advance(rx, n); return rx.base(); } }; From 5f6fd2dec633e814a6c6855775f70cd7ad545a66 Mon Sep 17 00:00:00 2001 From: Andrey Semashev Date: Sun, 23 Jul 2017 20:29:25 +0300 Subject: [PATCH 13/15] Use std::iterator_traits to detect iterators, when possible. This allows next/prior to detect user's iterators that do not define iterator_category nested type but specialize std::iterator_traits instead. --- include/boost/next_prior.hpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/include/boost/next_prior.hpp b/include/boost/next_prior.hpp index bad46ed..178cf67 100644 --- a/include/boost/next_prior.hpp +++ b/include/boost/next_prior.hpp @@ -52,7 +52,16 @@ struct is_iterator }; template< typename T > -struct is_iterator< T, typename enable_if_has_type< typename T::iterator_category >::type > +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; }; From a48de6a8b8ef9b1a8f75d023d4ddf2b2e43cace6 Mon Sep 17 00:00:00 2001 From: Andrey Semashev Date: Sat, 26 Aug 2017 15:31:38 +0300 Subject: [PATCH 14/15] Added tests for next/prior to the Jamfile. Ported tests to lightweight_test.hpp. --- test/Jamfile.v2 | 1 + test/next_prior_test.cpp | 41 ++++++++++++++++++++-------------------- 2 files changed, 21 insertions(+), 21 deletions(-) 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 index cd2812f..734a212 100644 --- a/test/next_prior_test.cpp +++ b/test/next_prior_test.cpp @@ -18,8 +18,7 @@ // simple functions, though, and it would be very strange if these // tests were to fail. -#define BOOST_INCLUDE_MAIN -#include +#include #include #include @@ -73,32 +72,32 @@ bool minus_n_unsigned_test(Iterator first, Iterator last, Distance size) return i == first; } -int test_main(int, char*[]) +int main(int, char*[]) { std::vector x(8); std::list y(x.begin(), x.end()); // Tests with iterators - BOOST_REQUIRE(plus_one_test(x.begin(), x.end(), y.begin())); - BOOST_REQUIRE(plus_n_test(x.begin(), x.end(), y.begin())); - BOOST_REQUIRE(minus_one_test(x.begin(), x.end(), y.end())); - BOOST_REQUIRE(minus_n_test(x.begin(), x.end(), y.end())); - BOOST_REQUIRE(minus_n_unsigned_test(x.begin(), x.end(), x.size())); - BOOST_REQUIRE(minus_n_unsigned_test(y.begin(), y.end(), y.size())); + 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_REQUIRE(plus_one_test(x.rbegin(), x.rend(), y.begin())); - BOOST_REQUIRE(plus_n_test(x.rbegin(), x.rend(), y.begin())); - BOOST_REQUIRE(minus_one_test(x.rbegin(), x.rend(), y.end())); - BOOST_REQUIRE(minus_n_test(x.rbegin(), x.rend(), y.end())); - BOOST_REQUIRE(minus_n_unsigned_test(x.rbegin(), x.rend(), x.size())); - BOOST_REQUIRE(minus_n_unsigned_test(x.rbegin(), x.rend(), 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_REQUIRE(boost::next(5) == 6); - BOOST_REQUIRE(boost::next(5, 7) == 12); - BOOST_REQUIRE(boost::prior(5) == 4); - BOOST_REQUIRE(boost::prior(5, 7) == -2); - BOOST_REQUIRE(boost::prior(5, 7u) == -2); + 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 0; + return boost::report_errors(); } From 5f6ac9c020f798eaa7fb0726246db07a511e5bf4 Mon Sep 17 00:00:00 2001 From: Andrey Semashev Date: Sat, 26 Aug 2017 16:27:55 +0300 Subject: [PATCH 15/15] Added next/prior documentation. --- doc/quickbook/algorithms.qbk | 63 ++++++++++++++++++++++++++++++++++++ doc/quickbook/iterator.qbk | 14 ++++++++ 2 files changed, 77 insertions(+) create mode 100644 doc/quickbook/algorithms.qbk 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]]