Last active
March 26, 2021 01:06
-
-
Save jeremy-rifkin/e875086a478301b59cddd8fd346b29c3 to your computer and use it in GitHub Desktop.
C++ range iterator
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <cmath> | |
#include <cassert> | |
#include <type_traits> | |
template<typename T> int sign(T t) { | |
return t < 0 ? -1 : 1; | |
} | |
template<typename T> class range { | |
typedef typename std::make_signed<T>::type ST; | |
T start; | |
T stop; // stored inclusively internally | |
ST step; | |
bool is_inclusive = false; | |
bool is_reversed = false; | |
public: | |
range(T stop) : range(0, stop) {} | |
range(T start, T stop) : range(start, stop, 1) {} | |
range(T start, T stop, ST step) : start(start), stop(stop + (start < stop ? -1 : 1)), step(step) { | |
// check bad range | |
assert(step != 0); | |
if(start < stop) assert(step > 0); // forwards range | |
if(stop < start) assert(step < 0); // null range or backwards range | |
} | |
private: | |
range(T start, T stop, ST step, bool is_inclusive, bool is_reversed) | |
: start(start), stop(stop), step(step), is_inclusive(is_inclusive), is_reversed(is_reversed) {} | |
public: | |
range reverse() { | |
return *this = range(get_end(), get_start(), -step, is_inclusive, !is_reversed); | |
} | |
range inclusive() { | |
if(!is_inclusive) { | |
if(is_reversed) { | |
*this = range(start - sign(step), stop, step, true, is_reversed); | |
} else { | |
*this = range(start, stop + sign(step), step, true, is_reversed); | |
} | |
} | |
return *this; | |
} | |
T size() const { | |
return std::abs(get_past_end() - get_start()); | |
} | |
private: | |
T get_start() const { | |
return start; | |
} | |
T get_end() const { | |
if(start < stop) { | |
return start + (stop - start) / step; | |
} else { | |
return start - (stop - start) / step; | |
} | |
} | |
T get_past_end() const { | |
return get_end() + sign(step); | |
} | |
public: | |
class const_iterator { | |
T i; | |
const range& r; | |
public: | |
const_iterator(const range& r, int i) : i(i), r(r) {} | |
const_iterator operator++() { auto i = *this; operator++(0); return i; } | |
const_iterator operator++(int) { i += r.step; return *this; } | |
T operator*() const { return i; } | |
bool operator==(const const_iterator& o) const { return i == o.i; } | |
bool operator!=(const const_iterator& o) const { return !operator==(o); } | |
}; | |
const_iterator begin() const { return const_iterator(*this, get_start()); } | |
const_iterator end() const { return const_iterator(*this, get_past_end()); } | |
}; | |
// example: | |
// for(auto i : range(12)) { | |
// ... | |
// } | |
// for(auto i : range(12).inclusive().reverse()) { | |
// ... | |
// } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment