From 33eb23e81a99f1dc19ab5e1024d3c90629934bab Mon Sep 17 00:00:00 2001 From: Alzathar Date: Sat, 21 Nov 2020 12:50:58 -0500 Subject: [PATCH] New method range_proxy::size() to know the size of the range. (#3) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * New method range_proxy::size() to know the size of the range. * Method range_proxy::step_range_proxy::size() Implementation updated from the PR discussion. Tests were added to verify the results * Use name “iterator” consistently * Simplify `size` implementation Co-authored-by: Arnaud Barré Co-authored-by: Konrad Rudolph --- range.hpp | 149 ++++++++++++++++++++++++++++++------------------------ test.cpp | 45 +++++++++++++++++ 2 files changed, 127 insertions(+), 67 deletions(-) diff --git a/range.hpp b/range.hpp index 90ca38a..83c0f72 100644 --- a/range.hpp +++ b/range.hpp @@ -1,6 +1,7 @@ #ifndef UTIL_LANG_RANGE_HPP #define UTIL_LANG_RANGE_HPP +#include #include #include @@ -42,58 +43,69 @@ struct range_iter_base : std::iterator { } // namespace detail template -struct range_proxy { +struct step_range_proxy { struct iterator : detail::range_iter_base { - iterator(T current) : detail::range_iter_base(current) { } - }; + iterator(T current, T step) + : detail::range_iter_base(current), step_(step) { } - struct step_range_proxy { - struct iter : detail::range_iter_base { - iter(T current, T step) - : detail::range_iter_base(current), step(step) { } + using detail::range_iter_base::current; - using detail::range_iter_base::current; + iterator& operator ++() { + current += step_; + return *this; + } - iter& operator ++() { - current += step; - return *this; - } + iterator operator ++(int) { + auto copy = *this; + ++*this; + return copy; + } - iter operator ++(int) { - auto copy = *this; - ++*this; - return copy; - } + // Loses commutativity. Iterator-based ranges are simply broken. :-( + bool operator ==(iterator const& other) const { + return step_ > 0 ? current >= other.current + : current < other.current; + } - // Loses commutativity. Iterator-based ranges are simply broken. :-( - bool operator ==(iter const& other) const { - return step > 0 ? current >= other.current - : current < other.current; - } + bool operator !=(iterator const& other) const { + return not (*this == other); + } - bool operator !=(iter const& other) const { - return not (*this == other); - } + T step_; + }; - private: - T step; - }; + step_range_proxy(T begin, T end, T step) + : begin_(begin, step), end_(end, step) { } - step_range_proxy(T begin, T end, T step) - : begin_(begin, step), end_(end, step) { } + iterator begin() const { return begin_; } - iter begin() const { return begin_; } + iterator end() const { return end_; } - iter end() const { return end_; } + std::size_t size() const { + if (*end_ >= *begin_) { + // Increasing and empty range + if (begin_.step_ < T{0}) return 0; + } else { + // Decreasing range + if (begin_.step_ > T{0}) return 0; + } + return std::ceil(std::abs(static_cast(*end_ - *begin_) / begin_.step_)); + } - private: - iter begin_; - iter end_; +private: + iterator begin_; + iterator end_; +}; + +template +struct range_proxy { + struct iterator : detail::range_iter_base { + iterator(T current) : detail::range_iter_base(current) { } }; range_proxy(T begin, T end) : begin_(begin), end_(end) { } - step_range_proxy step(T step) { + step_range_proxy step(T step) { return {*begin_, *end_, step}; } @@ -101,61 +113,64 @@ struct range_proxy { iterator end() const { return end_; } + std::size_t size() const { return *end_ - *begin_; } + private: iterator begin_; iterator end_; }; template -struct infinite_range_proxy { +struct step_inf_range_proxy { struct iterator : detail::range_iter_base { - iterator(T current = T()) : detail::range_iter_base(current) { } + iterator(T current = T(), T step = T()) + : detail::range_iter_base(current), step(step) { } - bool operator ==(iterator const&) const { return false; } + using detail::range_iter_base::current; - bool operator !=(iterator const&) const { return true; } - }; + iterator& operator ++() { + current += step; + return *this; + } - struct step_range_proxy { - struct iter : detail::range_iter_base { - iter(T current = T(), T step = T()) - : detail::range_iter_base(current), step(step) { } + iterator operator ++(int) { + auto copy = *this; + ++*this; + return copy; + } - using detail::range_iter_base::current; + bool operator ==(iterator const&) const { return false; } - iter& operator ++() { - current += step; - return *this; - } + bool operator !=(iterator const&) const { return true; } - iter operator ++(int) { - auto copy = *this; - ++*this; - return copy; - } + private: + T step; + }; - bool operator ==(iter const&) const { return false; } + step_inf_range_proxy(T begin, T step) : begin_(begin, step) { } - bool operator !=(iter const&) const { return true; } + iterator begin() const { return begin_; } - private: - T step; - }; + iterator end() const { return iterator(); } - step_range_proxy(T begin, T step) : begin_(begin, step) { } +private: + iterator begin_; +}; - iter begin() const { return begin_; } +template +struct infinite_range_proxy { + struct iterator : detail::range_iter_base { + iterator(T current = T()) : detail::range_iter_base(current) { } - iter end() const { return iter(); } + bool operator ==(iterator const&) const { return false; } - private: - iter begin_; + bool operator !=(iterator const&) const { return true; } }; infinite_range_proxy(T begin) : begin_(begin) { } - step_range_proxy step(T step) { - return step_range_proxy(*begin_, step); + step_inf_range_proxy step(T step) { + return {*begin_, step}; } iterator begin() const { return begin_; } diff --git a/test.cpp b/test.cpp index 1ee5533..d9edc09 100644 --- a/test.cpp +++ b/test.cpp @@ -12,6 +12,45 @@ void print_range(R const& range) { std::cout << "\n"; } +template +std::size_t manual_range_size(R const& range) { + std::size_t size = 0; + for (auto const& _ : range) ++size, (void) _; + return size; +} + +namespace util { namespace lang { + +// template +// struct is_a_step_range : std::false_type {}; + +// template +// struct is_a_step_range::step_range_proxy> : std::true_type {}; + +template +std::ostream& operator <<(std::ostream& out, step_range_proxy const& r) { + return out << "range(" << *r.begin() << ", " << *r.end() << ")" + << ".step(" << r.begin().step_ << ")"; +} + +template +std::ostream& operator <<(std::ostream& out, range_proxy const& r) { + return out << "range(" << *r.begin() << ", " << *r.end() << ")"; +} + +}} + +template +void test_range_size(R const& range) { + auto const real_size = manual_range_size(R{range}); + if (real_size == range.size()) { + std::cout << range << ".size() = " << real_size << "\n"; + } else { + std::cout << "ERROR: " << range << ".size() ≠ " << real_size + << " (was " << range.size() << ")!\n"; + } +} + int main() { using std::cout; using util::lang::range; @@ -64,4 +103,10 @@ int main() { print_range(range(8, 1).step(-2)); print_range(range(8.0, 1.0).step(-2.0)); cout << "\n"; + + test_range_size(range(1, 8).step(2)); + test_range_size(range(8.0, 1.0).step(-2.0)); + test_range_size(range(8, 1).step(-2)); + test_range_size(range(0.1, 0.11).step(2)); + test_range_size(range(-7, 1).step(7)); }