Merge pull request #30 from Lastique/fix_next_prior_for_iterators

Fix next/prior for iterators
This commit is contained in:
Andrey Semashev 2017-07-09 13:41:03 +03:00 committed by GitHub
commit ec50f22b8b
2 changed files with 78 additions and 55 deletions

View File

@ -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 <iterator>
#if defined(_MSC_VER) && _MSC_VER <= 1310
#include <boost/mpl/and.hpp>
#include <boost/type_traits/is_integral.hpp>
#endif
#include <boost/type_traits/is_unsigned.hpp>
#include <boost/type_traits/integral_promotion.hpp>
#include <boost/type_traits/make_signed.hpp>
#include <boost/config.hpp>
#include <boost/type_traits/has_plus.hpp>
#include <boost/type_traits/has_plus_assign.hpp>
#include <boost/type_traits/has_minus.hpp>
@ -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<T>::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 <class T>
@ -148,7 +164,7 @@ inline T next(T x) { return ++x; }
template <class T, class Distance>
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 <class T>
@ -157,7 +173,7 @@ inline T prior(T x) { return --x; }
template <class T, class Distance>
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

View File

@ -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);