/* * This file is part of the DXX-Rebirth project . * It is copyright by its individual contributors, as recorded in the * project's Git history. See COPYING.txt at the top level for license * terms and a link to the Git history. */ #pragma once #include #include "dxxsconf.h" #include "backports-ranges.h" #include #include namespace d_enumerate { namespace detail { /* In the general case, `value_type` is a tuple of the index and the * result of the underlying sequence. * * In the special case that `result_type` is a tuple, construct a * modified tuple instead of using a tuple of (index, inner tuple). * This allows simpler structured bindings. */ template struct adjust_iterator_dereference_type : std::false_type { using value_type = std::tuple; }; template struct adjust_iterator_dereference_type> : std::true_type { using value_type = std::tuple; }; /* Retrieve the member typedef `index_type` if one exists. Otherwise, * use `std::size_t` as a fallback. */ std::size_t array_index_type(...); /* Add, then remove, a reference to the `index_type`. If `index_type` * is an integer or enum type, this produces `index_type` again. If * `index_type` is `void`, then this overload refers to * `std::remove_reference::type`, which is ill-formed, and the * overload is removed by SFINAE. This allows target types to use * `using index_type = void` to decline to specify an `index_type`, * which may be convenient in templates that conditionally define an * `index_type` based on their template parameters. */ template typename std::remove_reference::type array_index_type(T *); } } template class enumerated_sentinel { public: sentinel_type m_sentinel; constexpr enumerated_sentinel() = default; constexpr enumerated_sentinel(sentinel_type &&iter) : m_sentinel(std::move(iter)) { } }; template class enumerated_iterator { range_iterator_type m_iter; range_index_type m_idx; public: using index_type = range_index_type; using iterator_category = std::forward_iterator_tag; using value_type = typename adjust_iterator_dereference_type::value_type; using difference_type = std::ptrdiff_t; using pointer = value_type *; using reference = value_type &; constexpr enumerated_iterator(range_iterator_type &&iter, const index_type idx) : m_iter(std::move(iter)), m_idx(idx) { } value_type operator*() const { if constexpr (adjust_iterator_dereference_type::value) return std::tuple_cat(std::tuple(m_idx), *m_iter); else return {m_idx, *m_iter}; } enumerated_iterator &operator++() { ++ m_iter; if constexpr (std::is_enum::value) /* An enum type is not required to have operator++() * defined, and may deliberately omit it to avoid allowing * the value to be incremented in the typical use case. For * this situation, an increment is desired, so emulate * operator++() by casting to the underlying integer * type, adding 1, and casting back. */ m_idx = static_cast(1u + static_cast::type>(m_idx)); else ++ m_idx; return *this; } enumerated_iterator operator++(int) { auto result = *this; ++ * this; return result; } constexpr bool operator==(const enumerated_sentinel &i) const { return m_iter == i.m_sentinel; } }; template class enumerate : ranges::subrange { using base_type = ranges::subrange; using iterator_dereference_type = decltype(*std::declval()); using enumerated_iterator_type = enumerated_iterator< range_index_type, range_iterator_type, range_sentinel_type, d_enumerate::detail::adjust_iterator_dereference_type::type>>; const range_index_type m_idx; public: using index_type = range_index_type; template /* Block using `enumerate` on an ephemeral range, since the storage * owned by the range must exist until the `enumerate` object is * fully consumed. If `range_type &&` is an ephemeral range, then its * storage may cease to exist after this constructor returns. */ requires(ranges::borrowed_range) enumerate(range_type &&t, const index_type i = index_type{}) : base_type(std::forward(t)), m_idx(i) { static_assert(std::is_rvalue_reference::value || !std::is_rvalue_reference::value, "lvalue range must not produce rvalue reference enumerated_value"); } enumerated_iterator_type begin() const { return {this->base_type::begin(), m_idx}; } enumerated_sentinel end() const { return {this->base_type::end()}; } }; template inline constexpr bool std::ranges::enable_borrowed_range> = true; template ::type *>(nullptr)))> requires(ranges::borrowed_range) enumerate(range_type &&r, index_type start = {/* value ignored for deduction guide */}) -> enumerate())), /* range_sentinel_type = */ decltype(std::ranges::end(std::declval())), index_type>;