diff --git a/src/util/iterutil.hpp b/src/util/iterutil.hpp index a443bf97c733fda54d157bc65fbce61ea46ce039..a357b667b488a8796520e70a6e84901f61e65033 100644 --- a/src/util/iterutil.hpp +++ b/src/util/iterutil.hpp @@ -43,16 +43,19 @@ upto(I iter, E end) { return iter==I{end}? iter: I{std::prev(end)}; } - -template <typename I, typename E> -enable_if_t<std::is_same<I, E>::value && is_forward_iterator<I>::value, - typename std::iterator_traits<I>::difference_type> +template <typename I, typename E, + typename C = typename common_random_access_iterator<I,E>::type> +enable_if_t<std::is_same<I, E>::value || + (has_common_random_access_iterator<I,E>::value && + is_forward_iterator<I>::value), + typename std::iterator_traits<C>::difference_type> distance(I first, E last) { - return std::distance(first, last); + return std::distance(static_cast<C>(first), static_cast<C>(last)); } template <typename I, typename E> -enable_if_t<!std::is_same<I, E>::value && is_forward_iterator<I>::value, +enable_if_t<!has_common_random_access_iterator<I, E>::value && + is_forward_iterator<I>::value, typename std::iterator_traits<I>::difference_type> distance(I first, E last) { typename std::iterator_traits<I>::difference_type ret = 0; diff --git a/src/util/meta.hpp b/src/util/meta.hpp index 40f08d481f23199e8e06a47fc41acdd58fdab8c9..e45d31604d08b53dea1a722cd329c241ffa4da36 100644 --- a/src/util/meta.hpp +++ b/src/util/meta.hpp @@ -151,6 +151,36 @@ struct is_forward_iterator<T, enable_if_t< template <typename T> using is_forward_iterator_t = typename is_forward_iterator<T>::type; + +template <typename I, typename E, typename = void, typename = void> +struct common_random_access_iterator {}; + +template <typename I, typename E> +struct common_random_access_iterator< + I, + E, + void_t<decltype(false ? std::declval<I>() : std::declval<E>())>, + enable_if_t< + is_random_access_iterator< + decay_t<decltype(false ? std::declval<I>() : std::declval<E>())> + >::value + > +> { + using type = decay_t< + decltype(false ? std::declval<I>() : std::declval<E>()) + >; +}; + +template <typename I, typename E, typename = void> +struct has_common_random_access_iterator: public std::false_type {}; + +template <typename I, typename E> +struct has_common_random_access_iterator< + I, E, void_t<typename common_random_access_iterator<I, E>::type> +> : public std::true_type {}; + + + } // namespace util } // namespace mc } // namespace nest diff --git a/tests/unit/test_range.cpp b/tests/unit/test_range.cpp index 0490928f70e0daffb9400005fcd2b03f37db2943..5b1d84cb97dd72f0dd0cad36afdc27bc389437a8 100644 --- a/tests/unit/test_range.cpp +++ b/tests/unit/test_range.cpp @@ -12,6 +12,7 @@ #endif #include <util/counter.hpp> +#include <util/meta.hpp> #include <util/range.hpp> #include <util/sentinel.hpp> @@ -20,7 +21,7 @@ using namespace nest::mc; TEST(range, list_iterator) { std::list<int> l = { 2, 4, 6, 8, 10 }; - auto s = util::make_range(l.begin(), l.end()); + auto s = util::make_range(l.begin(), l.end()); EXPECT_EQ(s.left, l.begin()); EXPECT_EQ(s.right, l.end()); @@ -42,6 +43,10 @@ TEST(range, list_iterator) { auto sum2 = std::accumulate(s.begin(), s.end(), 0); EXPECT_EQ(check, sum2); + + // Check that different begin/end iterators are treated correctly + auto sc = util::make_range(l.begin(), l.cend()); + EXPECT_EQ(l.size(), sc.size()); } TEST(range, pointer) { @@ -77,24 +82,54 @@ TEST(range, empty) { auto l = 2; auto r = 5; - auto empty_range_ll = util::make_range(&xs[l], &xs[l]); + auto empty_range_ll = util::make_range((const int *) &xs[l], &xs[l]); EXPECT_TRUE(empty_range_ll.empty()); EXPECT_EQ(empty_range_ll.begin() == empty_range_ll.end(), empty_range_ll.empty()); EXPECT_EQ(0u, empty_range_ll.size()); - auto empty_range_rr = util::make_range(&xs[r], &xs[r]); + auto empty_range_rr = util::make_range(&xs[r], (const int *) &xs[r]); EXPECT_TRUE(empty_range_rr.empty()); EXPECT_EQ(empty_range_rr.begin() == empty_range_rr.end(), empty_range_rr.empty()); EXPECT_EQ(0u, empty_range_rr.size()); } +template<typename I, typename E, typename = void> +struct util_distance_enabled : public std::false_type {}; + +// This is the same test for enabling util::distance +template<typename I, typename E> +struct util_distance_enabled< + I, E, util::void_t< + util::enable_if_t< + !util::has_common_random_access_iterator<I, E>::value && + util::is_forward_iterator<I>::value + >>> : public std::true_type {}; + +TEST(range, size) { + static_assert(util_distance_enabled< + typename std::list<int>::iterator, + typename std::list<int>::const_iterator>::value, + "util::distance not enabled"); + static_assert(!util_distance_enabled< + typename std::vector<int>::const_iterator, + typename std::vector<int>::iterator>::value, + "util::distance erroneously enabled"); + static_assert(!util_distance_enabled<int*, int*>::value, + "util::distance erroneously enabled"); + static_assert(!util_distance_enabled<int*, const int*>::value, + "util::distance erroneously enabled"); + static_assert(!util_distance_enabled<const int*, int*>::value, + "util::distance erroneously enabled"); +} + TEST(range, input_iterator) { int nums[] = { 10, 9, 8, 7, 6 }; std::istringstream sin("10 9 8 7 6"); - auto s = util::make_range(std::istream_iterator<int>(sin), std::istream_iterator<int>()); + auto s = util::make_range(std::istream_iterator<int>(sin), + std::istream_iterator<int>()); EXPECT_TRUE(std::equal(s.begin(), s.end(), &nums[0])); }