diff --git a/src/util/range.hpp b/src/util/range.hpp index 2439ca74ee785fac1f909e92b3189bbfcd6f5da2..9cb8a84a1706785ef05934129efe3327048c26cb 100644 --- a/src/util/range.hpp +++ b/src/util/range.hpp @@ -35,6 +35,7 @@ #include <util/either.hpp> #include <util/iterutil.hpp> #include <util/meta.hpp> +#include <util/sentinel.hpp> namespace nest { namespace mc { @@ -66,7 +67,7 @@ struct range { range& operator=(const range&) = default; range& operator=(range&&) = default; - bool empty() const { return left==right; } + bool empty() const { return size() == 0; } iterator begin() const { return left; } const_iterator cbegin() const { return left; } @@ -77,7 +78,10 @@ struct range { template <typename V = iterator> enable_if_t<is_forward_iterator<V>::value, size_type> size() const { - return std::distance(begin(), end()); + auto b = make_sentinel_iterator(begin(), end()); + auto e = make_sentinel_end(begin(), end()); + auto dist = std::distance(b, e); + return (dist < 0) ? 0 : dist; } constexpr size_type max_size() const { return std::numeric_limits<size_type>::max(); } @@ -148,164 +152,6 @@ range<U, V> make_range(const U& left, const V& right) { return range<U, V>(left, right); } -/* - * Use a proxy iterator to present a range as having the same begin and - * end types, for use with e.g. pre-C++17 ranged-for loops or STL - * algorithms. - */ -template <typename I, typename S> -class sentinel_iterator { - nest::mc::util::either<I, S> e_; - - bool is_sentinel() const { return e_.index()!=0; } - - I& iter() { - EXPECTS(!is_sentinel()); - return e_.template unsafe_get<0>(); - } - - const I& iter() const { - EXPECTS(!is_sentinel()); - return e_.template unsafe_get<0>(); - } - - S& sentinel() { - EXPECTS(is_sentinel()); - return e_.template unsafe_get<1>(); - } - - const S& sentinel() const { - EXPECTS(is_sentinel()); - return e_.template unsafe_get<1>(); - } - -public: - using difference_type = typename std::iterator_traits<I>::difference_type; - using value_type = typename std::iterator_traits<I>::value_type; - using pointer = typename std::iterator_traits<I>::pointer; - using reference = typename std::iterator_traits<I>::reference; - using iterator_category = typename std::iterator_traits<I>::iterator_category; - - sentinel_iterator(I i): e_(i) {} - - template <typename V = S, typename = enable_if_t<!std::is_same<I, V>::value>> - sentinel_iterator(S i): e_(i) {} - - sentinel_iterator() = default; - sentinel_iterator(const sentinel_iterator&) = default; - sentinel_iterator(sentinel_iterator&&) = default; - - sentinel_iterator& operator=(const sentinel_iterator&) = default; - sentinel_iterator& operator=(sentinel_iterator&&) = default; - - // forward and input iterator requirements - - auto operator*() const -> decltype(*(this->iter())) { return *iter(); } - - I operator->() const { return e_.template ptr<0>(); } - - sentinel_iterator& operator++() { - ++iter(); - return *this; - } - - sentinel_iterator operator++(int) { - sentinel_iterator c(*this); - ++*this; - return c; - } - - bool operator==(const sentinel_iterator& x) const { - if (is_sentinel()) { - return x.is_sentinel() || x.iter()==sentinel(); - } - else { - return x.is_sentinel()? iter()==x.sentinel(): iter()==x.iter(); - } - } - - bool operator!=(const sentinel_iterator& x) const { - return !(*this==x); - } - - // bidirectional iterator requirements - - sentinel_iterator& operator--() { - --iter(); - return *this; - } - - sentinel_iterator operator--(int) { - sentinel_iterator c(*this); - --*this; - return c; - } - - // random access iterator requirements - - sentinel_iterator &operator+=(difference_type n) { - iter() += n; - return *this; - } - - sentinel_iterator operator+(difference_type n) const { - sentinel_iterator c(*this); - return c += n; - } - - friend sentinel_iterator operator+(difference_type n, sentinel_iterator x) { - return x+n; - } - - sentinel_iterator& operator-=(difference_type n) { - iter() -= n; - return *this; - } - - sentinel_iterator operator-(difference_type n) const { - sentinel_iterator c(*this); - return c -= n; - } - - difference_type operator-(sentinel_iterator x) const { - return iter()-x.iter(); - } - - auto operator[](difference_type n) const -> decltype(*(this->iter())) { - return *(iter()+n); - } - - bool operator<=(const sentinel_iterator& x) const { - return x.is_sentinel() || (!is_sentinel() && iter()<=x.iter()); - } - - bool operator<(const sentinel_iterator& x) const { - return !is_sentinel() && (x.is_sentinel() || iter()<=x.iter()); - } - - bool operator>=(const sentinel_iterator& x) const { - return !(x<*this); - } - - bool operator>(const sentinel_iterator& x) const { - return !(x<=*this); - } -}; - -template <typename I, typename S> -using sentinel_iterator_t = - typename std::conditional<std::is_same<I, S>::value, I, sentinel_iterator<I, S>>::type; - -template <typename I, typename S> -sentinel_iterator_t<I, S> make_sentinel_iterator(const I& i, const S& s) { - return sentinel_iterator_t<I, S>(i); -} - -template <typename I, typename S> -sentinel_iterator_t<I, S> make_sentinel_end(const I& i, const S& s) { - return sentinel_iterator_t<I, S>(s); -} - template <typename Seq> auto canonical_view(const Seq& s) -> range<sentinel_iterator_t<decltype(std::begin(s)), decltype(std::end(s))>> diff --git a/src/util/sentinel.hpp b/src/util/sentinel.hpp new file mode 100644 index 0000000000000000000000000000000000000000..8d08ecd41fe77f9c11ed08b8b539536792459c36 --- /dev/null +++ b/src/util/sentinel.hpp @@ -0,0 +1,184 @@ +#pragma once + +#include <type_traits> + +#include <util/meta.hpp> + +/* + * Use a proxy iterator to present a range as having the same begin and + * end types, for use with e.g. pre-C++17 ranged-for loops or STL + * algorithms. + */ + +namespace nest { +namespace mc { +namespace util { + +template<typename I, typename S, bool Same> +struct iterator_category_select { + // default category + using iterator_category = std::forward_iterator_tag; +}; + +template<typename I, typename S> +struct iterator_category_select<I,S,true> { + using iterator_category = typename std::iterator_traits<I>::iterator_category; +}; + +template <typename I, typename S> +class sentinel_iterator { + nest::mc::util::either<I, S> e_; + + bool is_sentinel() const { return e_.index()!=0; } + + I& iter() { + EXPECTS(!is_sentinel()); + return e_.template unsafe_get<0>(); + } + + const I& iter() const { + EXPECTS(!is_sentinel()); + return e_.template unsafe_get<0>(); + } + + S& sentinel() { + EXPECTS(is_sentinel()); + return e_.template unsafe_get<1>(); + } + + const S& sentinel() const { + EXPECTS(is_sentinel()); + return e_.template unsafe_get<1>(); + } + +public: + using difference_type = typename std::iterator_traits<I>::difference_type; + using value_type = typename std::iterator_traits<I>::value_type; + using pointer = typename std::iterator_traits<I>::pointer; + using reference = typename std::iterator_traits<I>::reference; + using iterator_category = typename iterator_category_select< + I,S,std::is_same<I,S>::value>::iterator_category; + + sentinel_iterator(I i): e_(i) {} + + template <typename V = S, typename = enable_if_t<!std::is_same<I, V>::value>> + sentinel_iterator(S i): e_(i) {} + + sentinel_iterator() = default; + sentinel_iterator(const sentinel_iterator&) = default; + sentinel_iterator(sentinel_iterator&&) = default; + + sentinel_iterator& operator=(const sentinel_iterator&) = default; + sentinel_iterator& operator=(sentinel_iterator&&) = default; + + // forward and input iterator requirements + + auto operator*() const -> decltype(*(this->iter())) { return *iter(); } + + I operator->() const { return e_.template ptr<0>(); } + + sentinel_iterator& operator++() { + ++iter(); + return *this; + } + + sentinel_iterator operator++(int) { + sentinel_iterator c(*this); + ++*this; + return c; + } + + bool operator==(const sentinel_iterator& x) const { + if (is_sentinel()) { + return x.is_sentinel() || x.iter()==sentinel(); + } + else { + return x.is_sentinel()? iter()==x.sentinel(): iter()==x.iter(); + } + } + + bool operator!=(const sentinel_iterator& x) const { + return !(*this==x); + } + + // bidirectional iterator requirements + + sentinel_iterator& operator--() { + --iter(); + return *this; + } + + sentinel_iterator operator--(int) { + sentinel_iterator c(*this); + --*this; + return c; + } + + // random access iterator requirements + + sentinel_iterator &operator+=(difference_type n) { + iter() += n; + return *this; + } + + sentinel_iterator operator+(difference_type n) const { + sentinel_iterator c(*this); + return c += n; + } + + friend sentinel_iterator operator+(difference_type n, sentinel_iterator x) { + return x+n; + } + + sentinel_iterator& operator-=(difference_type n) { + iter() -= n; + return *this; + } + + sentinel_iterator operator-(difference_type n) const { + sentinel_iterator c(*this); + return c -= n; + } + + difference_type operator-(sentinel_iterator x) const { + return iter()-x.iter(); + } + + auto operator[](difference_type n) const -> decltype(*(this->iter())) { + return *(iter()+n); + } + + bool operator<=(const sentinel_iterator& x) const { + return x.is_sentinel() || (!is_sentinel() && iter()<=x.iter()); + } + + bool operator<(const sentinel_iterator& x) const { + return !is_sentinel() && (x.is_sentinel() || iter()<=x.iter()); + } + + bool operator>=(const sentinel_iterator& x) const { + return !(x<*this); + } + + bool operator>(const sentinel_iterator& x) const { + return !(x<=*this); + } +}; + +template <typename I, typename S> +using sentinel_iterator_t = + typename std::conditional<std::is_same<I, S>::value, I, sentinel_iterator<I, S>>::type; + +template <typename I, typename S> +sentinel_iterator_t<I, S> make_sentinel_iterator(const I& i, const S& s) { + return sentinel_iterator_t<I, S>(i); +} + +template <typename I, typename S> +sentinel_iterator_t<I, S> make_sentinel_end(const I& i, const S& s) { + return sentinel_iterator_t<I, S>(s); +} + +} +} +} diff --git a/tests/unit/test_range.cpp b/tests/unit/test_range.cpp index 7847212e3f0b0546c1f974dbdebab3303a15ea1d..af6ac52df08892cc337369612ba46500a8d120b9 100644 --- a/tests/unit/test_range.cpp +++ b/tests/unit/test_range.cpp @@ -12,6 +12,7 @@ #endif #include <util/range.hpp> +#include <util/sentinel.hpp> using namespace nest::mc; @@ -70,6 +71,17 @@ TEST(range, pointer) { EXPECT_TRUE(std::equal(s.begin(), s.end(), &xs[l])); } +TEST(range, empty) { + int xs[] = { 10, 11, 12, 13, 14, 15, 16 }; + auto l = 2; + auto r = 5; + + EXPECT_TRUE(util::make_range(&xs[l], &xs[l]).empty()); + EXPECT_TRUE(util::make_range(&xs[r], &xs[r]).empty()); + EXPECT_TRUE(util::make_range(&xs[r], &xs[l]).empty()); + EXPECT_EQ(0u, util::make_range(&xs[r], &xs[l]).size()); +} + TEST(range, input_iterator) { int nums[] = { 10, 9, 8, 7, 6 }; std::istringstream sin("10 9 8 7 6"); @@ -122,6 +134,10 @@ TEST(range, sentinel) { } EXPECT_EQ(s, std::string(cstr)); + + const char *empty_cstr = ""; + auto empty_cstr_range = util::make_range(empty_cstr, null_terminated); + EXPECT_TRUE(empty_cstr_range.empty()); } #ifdef WITH_TBB