Skip to content

Commit

Permalink
New method range_proxy::size() to know the size of the range. (#3)
Browse files Browse the repository at this point in the history
* 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é <alzathar@Moveck-C05.local>
Co-authored-by: Konrad Rudolph <konrad.rudolph@gmail.com>
  • Loading branch information
3 people authored Nov 21, 2020
1 parent 5411558 commit 33eb23e
Show file tree
Hide file tree
Showing 2 changed files with 127 additions and 67 deletions.
149 changes: 82 additions & 67 deletions range.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#ifndef UTIL_LANG_RANGE_HPP
#define UTIL_LANG_RANGE_HPP

#include <cmath>
#include <iterator>
#include <type_traits>

Expand Down Expand Up @@ -42,120 +43,134 @@ struct range_iter_base : std::iterator<std::input_iterator_tag, T> {
} // namespace detail

template <typename T>
struct range_proxy {
struct step_range_proxy {
struct iterator : detail::range_iter_base<T> {
iterator(T current) : detail::range_iter_base<T>(current) { }
};
iterator(T current, T step)
: detail::range_iter_base<T>(current), step_(step) { }

struct step_range_proxy {
struct iter : detail::range_iter_base<T> {
iter(T current, T step)
: detail::range_iter_base<T>(current), step(step) { }
using detail::range_iter_base<T>::current;

using detail::range_iter_base<T>::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<double>(*end_ - *begin_) / begin_.step_));
}

private:
iter begin_;
iter end_;
private:
iterator begin_;
iterator end_;
};

template <typename T>
struct range_proxy {
struct iterator : detail::range_iter_base<T> {
iterator(T current) : detail::range_iter_base<T>(current) { }
};

range_proxy(T begin, T end) : begin_(begin), end_(end) { }

step_range_proxy step(T step) {
step_range_proxy<T> step(T step) {
return {*begin_, *end_, step};
}

iterator begin() const { return begin_; }

iterator end() const { return end_; }

std::size_t size() const { return *end_ - *begin_; }

private:
iterator begin_;
iterator end_;
};

template <typename T>
struct infinite_range_proxy {
struct step_inf_range_proxy {
struct iterator : detail::range_iter_base<T> {
iterator(T current = T()) : detail::range_iter_base<T>(current) { }
iterator(T current = T(), T step = T())
: detail::range_iter_base<T>(current), step(step) { }

bool operator ==(iterator const&) const { return false; }
using detail::range_iter_base<T>::current;

bool operator !=(iterator const&) const { return true; }
};
iterator& operator ++() {
current += step;
return *this;
}

struct step_range_proxy {
struct iter : detail::range_iter_base<T> {
iter(T current = T(), T step = T())
: detail::range_iter_base<T>(current), step(step) { }
iterator operator ++(int) {
auto copy = *this;
++*this;
return copy;
}

using detail::range_iter_base<T>::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 <typename T>
struct infinite_range_proxy {
struct iterator : detail::range_iter_base<T> {
iterator(T current = T()) : detail::range_iter_base<T>(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<T> step(T step) {
return {*begin_, step};
}

iterator begin() const { return begin_; }
Expand Down
45 changes: 45 additions & 0 deletions test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,45 @@ void print_range(R const& range) {
std::cout << "\n";
}

template <typename R>
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 <typename T>
// struct is_a_step_range : std::false_type {};

// template <typename T>
// struct is_a_step_range<typename range_proxy<T>::step_range_proxy> : std::true_type {};

template <typename T>
std::ostream& operator <<(std::ostream& out, step_range_proxy<T> const& r) {
return out << "range(" << *r.begin() << ", " << *r.end() << ")"
<< ".step(" << r.begin().step_ << ")";
}

template <typename T>
std::ostream& operator <<(std::ostream& out, range_proxy<T> const& r) {
return out << "range(" << *r.begin() << ", " << *r.end() << ")";
}

}}

template <typename R>
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;
Expand Down Expand Up @@ -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));
}

0 comments on commit 33eb23e

Please sign in to comment.