Created
September 1, 2020 04:52
-
-
Save tame-64/a6fe22b0841039d682cbcf542d47cc27 to your computer and use it in GitHub Desktop.
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
// 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