#pragma once #if !defined(MIJIN_UTIL_ITERATORS_HPP_INCLUDED) #define MIJIN_UTIL_ITERATORS_HPP_INCLUDED 1 #include #include #include #include #include #include namespace mijin { struct RangeAdapter {}; template struct RangeRef { T& range; decltype(auto) begin() const { return std::begin(range); } decltype(auto) end() const { return std::end(range); } }; template struct is_range_adapter_type : std::false_type {}; template requires std::is_base_of_v struct is_range_adapter_type : std::true_type {}; // allow passing in spans and string_views by value template struct is_range_adapter_type> : std::true_type {}; template struct is_range_adapter_type> : std::true_type {}; template concept RangeAdapterType = is_range_adapter_type>::value; static_assert(!is_range_adapter_type>::value); static_assert(is_range_adapter_type::value); template struct RangeRef { std::remove_reference_t range; decltype(auto) begin() const { return range.begin(); } decltype(auto) end() const { return range.end(); } }; template struct EnumeratingIterator { TIdx idx; TIterator base; EnumeratingIterator(TIdx idx_, TIterator base_) noexcept : idx(idx_), base(base_) {} EnumeratingIterator(const EnumeratingIterator&) noexcept = default; EnumeratingIterator& operator=(const EnumeratingIterator&) noexcept = default; auto operator*() const noexcept { return std::tie(idx, *base); } EnumeratingIterator& operator++() noexcept { ++idx; ++base; return *this; } EnumeratingIterator operator++(int) noexcept { EnumeratingIterator copy(*this); ++(*this); return copy; } EnumeratingIterator& operator--() noexcept { --idx; --base; return *this; } EnumeratingIterator operator--(int) noexcept { EnumeratingIterator copy(*this); --(*this); return copy; } bool operator==(const EnumeratingIterator& other) const noexcept { return base == other.base; // note: ignoring idx so we don't have to find it out for end() } bool operator!=(const EnumeratingIterator& other) const noexcept { return base != other.base; // note: ignoring idx so we don't have to find it out for end() } }; template EnumeratingIterator(TIdx, TIterator) -> EnumeratingIterator; template struct Enumeratable : RangeAdapter { RangeRef base; auto begin() const noexcept { return EnumeratingIterator(TIdx(0), base.begin()); } auto end() const noexcept { return EnumeratingIterator(TIdx(0), base.end()); } }; template Enumeratable enumerate(TIterable&& iterable) { return {.base = {std::forward(iterable)}}; } template struct ReplacingIterator { using difference_type = std::ptrdiff_t; using value_type = typename std::iterator_traits::value_type; using pointer = std::add_const_t*; using reference = std::add_const_t&; using iterator_category = std::bidirectional_iterator_tag; // TODO? TIterator base; value_type what; value_type with; ReplacingIterator(TIterator base_, value_type what_, value_type with_) noexcept : base(base_), what(what_), with(with_) {} ReplacingIterator(const ReplacingIterator&) noexcept = default; ReplacingIterator& operator=(const ReplacingIterator&) noexcept = default; pointer operator->() const noexcept { if (*base == what) { return &with; } return &*base; } reference operator*() const noexcept { if (*base == what) { return with; } return *base; } ReplacingIterator& operator++() noexcept { ++base; return *this; } ReplacingIterator operator++(int) noexcept { ReplacingIterator copy(*this); ++(*this); return copy; } ReplacingIterator& operator--() noexcept { --base; return *this; } ReplacingIterator operator--(int) noexcept { EnumeratingIterator copy(*this); --(*this); return copy; } bool operator==(const ReplacingIterator& other) const noexcept { return what == other.what && with == other.with && base == other.base; } bool operator!=(const ReplacingIterator& other) const noexcept { return !(*this == other); } }; template struct ReplacingRange : RangeAdapter { using value_type = typename std::iterator_traits()))>::value_type; RangeRef base; value_type what; value_type with; auto begin() const noexcept { return ReplacingIterator(base.begin(), what, with); } auto end() const noexcept { return ReplacingIterator(base.end(), what, with); } }; template auto replace( TIterable&& iterable, typename std::iterator_traits()))>::value_type what, typename std::iterator_traits()))>::value_type with) { return ReplacingRange{ .base = {std::forward(iterable)}, .what = std::move(what), .with = std::move(with) }; } template struct MappingIterator { using orig_value_type = typename std::iterator_traits::value_type; using difference_type = std::ptrdiff_t; using value_type = std::invoke_result_t; using iterator_category = std::bidirectional_iterator_tag; // TODO? TIterator base; TFunctor functor; MappingIterator(TIterator base_, TFunctor functor_) noexcept : base(base_), functor(std::move(functor_)) {} MappingIterator(const MappingIterator&) noexcept = default; MappingIterator(MappingIterator&&) noexcept = default; MappingIterator& operator=(const MappingIterator&) noexcept = default; MappingIterator& operator=(MappingIterator&&) noexcept = default; value_type operator*() const noexcept { return functor(*base); } MappingIterator& operator++() noexcept { ++base; return *this; } MappingIterator operator++(int) noexcept { ReplacingIterator copy(*this); ++(*this); return copy; } MappingIterator& operator--() noexcept { --base; return *this; } MappingIterator operator--(int) noexcept { EnumeratingIterator copy(*this); --(*this); return copy; } bool operator==(const MappingIterator& other) const noexcept { return functor == other.functor && base == other.base; } bool operator!=(const MappingIterator& other) const noexcept { return !(*this == other); } }; template struct MappingRange : RangeAdapter { using value_type = typename std::iterator_traits()))>::value_type; RangeRef base; TFunctor functor; auto begin() const noexcept { return MappingIterator(base.begin(), functor); } auto end() const noexcept { return MappingIterator(base.end(), functor); } }; template auto map(TIterable&& iterable, TFunctor&& functor) { return MappingRange{ .base = {std::forward(iterable)}, .functor = std::forward(functor) }; } template struct ChainingIterator { using difference_type = std::ptrdiff_t; using value_type = typename std::iterator_traits::value_type; using pointer = std::add_const_t*; using reference = std::add_const_t&; using iterator_category = std::bidirectional_iterator_tag; // TODO? TFirstIterator firstBase; TFirstIterator firstEnd; TSecondIterator secondBase; TSecondIterator secondBegin; ChainingIterator(TFirstIterator firstBase_, TFirstIterator firstEnd_, TSecondIterator secondBase_, TSecondIterator secondBegin_) noexcept : firstBase(firstBase_), firstEnd(firstEnd_), secondBase(secondBase_), secondBegin(secondBegin_) {} ChainingIterator(const ChainingIterator&) noexcept = default; ChainingIterator& operator=(const ChainingIterator&) noexcept = default; pointer operator->() const noexcept { if (firstBase == firstEnd) { return &*secondBase; } return &*firstBase; } reference operator*() const noexcept { if (firstBase == firstEnd) { return *secondBase; } return *firstBase; } ChainingIterator& operator++() noexcept { if (firstBase == firstEnd) { ++secondBase; } else { ++firstBase; } return *this; } ChainingIterator operator++(int) noexcept { ChainingIterator copy(*this); ++(*this); return copy; } ChainingIterator& operator--() noexcept { if (secondBase == secondBegin) { --firstBase; } else { --secondBase; } return *this; } ChainingIterator operator--(int) noexcept { ChainingIterator copy(*this); --(*this); return copy; } bool operator==(const ChainingIterator& other) const noexcept { return firstBase == other.firstBase && secondBase == other.secondBase; // should be enough } bool operator!=(const ChainingIterator& other) const noexcept { return !(*this == other); } }; template struct ChainedRange : RangeAdapter { using value_type = typename std::iterator_traits()))>::value_type; RangeRef first; RangeRef second; auto begin() const noexcept { return ChainingIterator(first.begin(), first.end(), second.begin(), second.begin()); } auto end() const noexcept { return ChainingIterator(first.end(), first.end(), second.end(), second.begin()); } }; template auto chain(TFirstRange&& firstRange, TSecondRange&& secondRange) { return ChainedRange{ .first = {std::forward(firstRange)}, .second = {std::forward(secondRange)} }; } template auto chain(TFirstRange&& firstRange, TSecondRange&& secondRange, TMoreRanges&&... moreRanges) { return chain(std::forward(firstRange), chain(std::forward(secondRange), std::forward(moreRanges)...)); } template TAs collect(TIterable&& iterable) { return TAs(iterable.begin(), iterable.end()); } namespace pipe { template struct Replace { T what; T with; Replace(T what_, T with_) : what(std::move(what_)), with(std::move(with_)) {} }; template Replace(T, T) -> Replace; template auto operator|(TIterable&& iterable, Replace()))>::value_type> repl) { return replace(std::forward(iterable), std::move(repl.what), std::move(repl.with)); } template struct Collect {}; template auto operator|(TIterable&& iterable, Collect) { return collect(std::forward(iterable)); } struct Chain {}; template struct Chain2 { RangeRef range; }; template auto operator|(TIterable&& iterable, Chain) { return Chain2{.range = {.range = std::forward(iterable)}}; } template auto operator|(Chain2 chain2, TSecondIterable&& secondIterable) { return chain(chain2.range.range, std::forward(secondIterable)); } template struct Map { T functor; explicit Map(T functor_) : functor(std::move(functor_)) {} }; template Map(T) -> Map; template auto operator|(TIterable&& iterable, Map mapper) { return map(std::forward(iterable), std::move(mapper.functor)); } } } // namespace mijin #endif // MIJIN_UTIL_ITERATORS_HPP_INCLUDED