// SPDX-License-Identifier: BSD-2-Clause // author: Max Kellermann #pragma once #include // for std::iterator_traits /** * A container-like class which allows iterating over an array * terminated by sentinel value. Most commonly, this is an array of * pointers terminated by nullptr, but null-terminated C strings can * also be used. * * @param T the type of an item in the aray * @param Sentinel the sentinel value */ template class TerminatedArray { T *head; /** * A special type for the end iterator which is always equal * the iterator that points to the #Sentinel value. */ struct sentinel { using Traits = std::iterator_traits; using iterator_category = typename Traits::iterator_category; using difference_type = typename Traits::difference_type; using value_type = typename Traits::value_type; using pointer = typename Traits::pointer; using reference = typename Traits::reference; }; public: using value_type = T; explicit constexpr TerminatedArray(T *_head) noexcept :head(_head) {} class iterator { using Traits = std::iterator_traits; T *cursor; public: using iterator_category = typename Traits::iterator_category; using difference_type = typename Traits::difference_type; using value_type = typename Traits::value_type; using pointer = typename Traits::pointer; using reference = typename Traits::reference; explicit constexpr iterator(T *_cursor) noexcept :cursor(_cursor) {} constexpr bool operator==(const iterator &other) const noexcept { return cursor == other.cursor; } constexpr bool operator==(const sentinel &) const noexcept { return *cursor == Sentinel; } constexpr auto &operator++() noexcept { ++cursor; return *this; } constexpr auto operator++(int) noexcept { auto old = *this; ++cursor; return old; } constexpr auto &operator+=(difference_type n) noexcept { cursor += n; return *this; } constexpr auto operator+(difference_type n) const noexcept { return iterator{cursor + n}; } constexpr auto &operator--() noexcept { --cursor; return *this; } constexpr auto operator--(int) noexcept { auto old = *this; --cursor; return old; } constexpr auto &operator-=(difference_type n) noexcept { cursor -= n; return *this; } constexpr auto operator-(difference_type n) const noexcept { return iterator{cursor - n}; } constexpr auto operator-(const iterator& other) const noexcept { return std::distance(other.cursor, cursor); } reference operator*() const noexcept { return *cursor; } pointer operator->() const noexcept { return cursor; } }; constexpr iterator begin() const noexcept { return iterator{head}; } constexpr sentinel end() const noexcept { return {}; } constexpr iterator cbegin() const noexcept { return begin(); } constexpr sentinel cend() const noexcept { return end(); } };