Skip to content

Instantly share code, notes, and snippets.

@tame-64
Created September 1, 2020 04:52
Show Gist options
  • Save tame-64/a6fe22b0841039d682cbcf542d47cc27 to your computer and use it in GitHub Desktop.
Save tame-64/a6fe22b0841039d682cbcf542d47cc27 to your computer and use it in GitHub Desktop.
// compile with c++20 enabled
#pragma once
#include <iterator>
#include <limits>
#include <type_traits>
namespace tame {
template <typename, typename = void>
class range_t;
namespace detail {
template <typename T>
class const_range_t_iterator {
using parent = range_t<T>;
public:
using iterator_category = std::random_access_iterator_tag;
using value_type = T;
using difference_type = ptrdiff_t;
using pointer = void;
using reference = value_type; /* there are no reference, hence no '&' sign */
/* required for reverse iterator */
private:
const parent& m_parent;
ptrdiff_t m_index{ };
public:
constexpr const_range_t_iterator(const parent& _parent, ptrdiff_t index) noexcept
: m_parent{ _parent }, m_index{ index } { }
difference_type operator-(const const_range_t_iterator& rhs) const noexcept {
return static_cast<difference_type>(m_index - rhs.m_index);
}
constexpr value_type operator*() const noexcept {
return m_parent.at(m_index);
}
constexpr bool operator==(const const_range_t_iterator& other) const noexcept {
return (m_parent == other.m_parent) && (m_index == other.m_index);
}
constexpr bool operator!=(const const_range_t_iterator& other) const noexcept {
return (!operator==(other));
}
constexpr reference operator[](const ptrdiff_t off) const noexcept {
return *(*this + off);
}
constexpr const_range_t_iterator& operator++() noexcept {
m_index = std::min(m_index + 1, static_cast<ptrdiff_t>(m_parent.size()));
return *this;
}
constexpr const_range_t_iterator operator++(int) noexcept {
const_range_t_iterator temp = *this;
++* this;
return temp;
}
constexpr const_range_t_iterator& operator--() noexcept {
m_index = std::max(m_index - 1, 0);
return *this;
}
constexpr const_range_t_iterator operator--(int) noexcept {
const_range_t_iterator temp = *this;
--* this;
return temp;
}
constexpr const_range_t_iterator& operator+=(const ptrdiff_t off) noexcept {
m_index += _compat_offset(off);
return *this;
}
constexpr const_range_t_iterator operator+(const ptrdiff_t off) const noexcept {
const_range_t_iterator temp = *this;
return temp += off;
}
constexpr const_range_t_iterator& operator-=(const ptrdiff_t off) noexcept {
return *this += -off;
}
constexpr const_range_t_iterator operator-(const ptrdiff_t off) const noexcept {
const_range_t_iterator temp = *this;
return temp -= off;
}
constexpr bool operator<(const const_range_t_iterator& rhs) const {
return m_index < rhs.m_index;
}
constexpr bool operator>(const const_range_t_iterator& rhs) const {
return rhs < *this;
}
constexpr bool operator<=(const const_range_t_iterator& rhs) const {
return !(rhs < *this);
}
constexpr bool operator>=(const const_range_t_iterator& rhs) const {
return !(*this < rhs);
}
private:
constexpr ptrdiff_t _compat_offset(const ptrdiff_t off) {
if (off > m_parent.zero_value()) { // ++
return std::min(static_cast<ptrdiff_t>(m_parent.size()) - m_index, off);
}
else if (std::abs(off) > m_index) { // --
return m_parent.size() - m_index;
}
return off;
}
};
template <typename T>
class range_t_iterator : public const_range_t_iterator<T> {
using base = const_range_t_iterator<T>;
using parent = range_t<T>;
public:
constexpr range_t_iterator(parent& _parent, ptrdiff_t index) noexcept
: base(_parent, index) { };
using base::operator-;
constexpr range_t_iterator& operator++() noexcept {
base::operator++();
return *this;
}
constexpr range_t_iterator operator++(int) noexcept {
range_t_iterator temp = *this;
base::operator++();
return temp;
}
constexpr range_t_iterator& operator--() noexcept {
base::operator--();
return *this;
}
constexpr range_t_iterator operator--(int) noexcept {
range_t_iterator temp = *this;
base::operator--();
return temp;
}
constexpr range_t_iterator& operator+=(const ptrdiff_t off) noexcept {
base::operator+=(off);
return *this;
}
constexpr range_t_iterator operator+(const ptrdiff_t off) const noexcept {
range_t_iterator temp = *this;
return temp += off;
}
constexpr range_t_iterator& operator-=(const ptrdiff_t off) noexcept {
base::operator-=(off);
return *this;
}
constexpr range_t_iterator operator-(const ptrdiff_t off) const noexcept {
range_t_iterator temp = *this;
return temp -= off;
}
};
}
template <typename T, typename Ret = void>
struct enable_range : std::enable_if<std::is_floating_point_v<T> || std::is_integral_v<T>, Ret> { };
template <typename T, typename Ret = void>
using enable_range_t = typename enable_range<T, Ret>::type;
template <typename T>
class range_t<T, enable_range_t<T>> {
friend class detail::const_range_t_iterator<T>;
public:
using value_type = typename T;
using return_type = typename range_t<value_type>;
using iterator = detail::range_t_iterator<T>;
using const_iterator = detail::const_range_t_iterator<T>;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
private:
value_type m_begin;
value_type m_end;
value_type m_step;
size_t m_size{ std::numeric_limits<size_t>::max() };
public:
static constexpr bool valid_v = std::is_floating_point_v<value_type> || std::is_integral_v<value_type>;
static_assert(valid_v, "require T is integral type or floating point");
inline static constexpr value_type zero_value() noexcept {
return static_cast<value_type>(0);
}
inline static constexpr value_type one_value() noexcept {
return static_cast<value_type>(1);
}
constexpr range_t(const value_type& begin, const value_type& end, const value_type& step)
: m_begin{ begin }, m_end{ end }, m_step{ step }, m_size{ _size() } { }
constexpr bool operator==(const range_t& rhs) const {
return (m_begin == rhs.m_begin)
&& (m_end == rhs.m_end)
&& (m_step == rhs.m_step);
}
value_type operator[](const ptrdiff_t off) const noexcept {
return at(off);
}
constexpr bool empty() const { return size() == 0; }
constexpr size_t size() const { return m_size; }
constexpr value_type at(const ptrdiff_t off) const {
return (_valid_off(off)) ? _unsafe_at(off) : m_end;
}
constexpr value_type start() const { return m_begin; }
constexpr value_type stop() const { return m_end; }
constexpr value_type step() const { return m_step; }
value_type front() const { return m_begin; }
value_type back() const { return _unsafe_at(size() - 1); }
constexpr iterator begin() { return iterator(*this, 0); }
constexpr iterator end() { return iterator(*this, size()); }
constexpr const_iterator begin() const { return const_iterator(*this, 0); }
constexpr const_iterator end() const { return const_iterator(*this, size()); }
constexpr const_iterator cbegin() const { return begin(); }
constexpr const_iterator cend() const { return end(); }
constexpr reverse_iterator rbegin() noexcept { return reverse_iterator(end()); }
constexpr reverse_iterator rend() noexcept { return reverse_iterator(begin()); }
constexpr const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(end()); }
constexpr const_reverse_iterator rend() const noexcept { return const_reverse_iterator(begin()); }
constexpr const_reverse_iterator crbegin() const noexcept { return rbegin(); }
constexpr const_reverse_iterator crend() const noexcept { return rend(); }
private:
constexpr size_t _size() const noexcept {
if constexpr (std::is_floating_point_v<value_type>) {
if (m_step == zero_value()) {
return 0;
}
else if (m_step == one_value()) {
return static_cast<size_t>(m_end - m_begin);
}
}
else if constexpr (std::is_integral_v<value_type>) {
switch (m_step)
{
case 0: return 0;
case 1: return m_end - m_begin;
}
}
return ((m_step > zero_value()) || ((m_step < zero_value()) && (m_begin > m_end)))
? static_cast<size_t>((m_end - m_begin) / m_step)
: 0;
}
constexpr value_type _unsafe_at(const ptrdiff_t off) const {
return m_begin + (m_step * off);
}
constexpr bool _valid_off(const ptrdiff_t off) const {
return !empty() && (off >= 0) && (off < static_cast<decltype(off)>(size()));
}
};
template <typename T>
class range_t<T&> : public range_t<std::remove_cvref_t<T>> { };
template <typename T>
class range_t<T&&> : public range_t<std::remove_cvref_t<T>> { };
template <typename ...Ts>
struct _multi_range_t : range_t<std::common_type_t<Ts...>> { };
template <typename ...Ts>
using _enable_multi_range_t = std::enable_if_t<_multi_range_t<Ts...>::valid_v, typename _multi_range_t<Ts...>::return_type>;
template <typename T1, typename T2, typename T3>
constexpr _enable_multi_range_t<T1, T2, T3>
range(T1&& start, T2&& end, T3&& step)
{
using type_t = typename _multi_range_t<T1, T2, T3>::value_type;
return { static_cast<type_t>(start), static_cast<type_t>(end), static_cast<type_t>(step) };
}
template <typename T1, typename T2>
constexpr _enable_multi_range_t<T1, T2>
range(T1&& start, T2&& end)
{
using type_t = typename _multi_range_t<T1, T2>::value_type;
return { static_cast<type_t>(start), static_cast<type_t>(end), range_t<type_t>::one_value() };
}
namespace strict {
template <typename T>
constexpr _enable_multi_range_t<T> range(T&& start, T&& end, T&& step) {
using type_t = typename range_t<T>::value_type;
return { start, end, step };
}
template <typename T>
constexpr _enable_multi_range_t<T> range(T&& start, T&& end) {
using type_t = typename _multi_range_t<T>::value_type;
return { start, end, range_t<type_t>::one_value() };
}
}
template <typename T>
constexpr _enable_multi_range_t<T> range(T&& end) {
using type_t = typename _multi_range_t<T>::value_type;
return { range_t<type_t>::zero_value(), static_cast<type_t>(end), range_t<type_t>::one_value() };
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment