mijin2/source/mijin/util/iterators.hpp

977 lines
26 KiB
C++

#pragma once
#if !defined(MIJIN_UTIL_ITERATORS_HPP_INCLUDED)
#define MIJIN_UTIL_ITERATORS_HPP_INCLUDED 1
#include <cstddef>
#include <functional>
#include <optional>
#include <span>
#include <string_view>
#include <tuple>
#include <variant>
#include "../container/optional.hpp"
#include "../internal/common.hpp"
namespace mijin
{
struct RangeAdapter {};
template<typename T>
struct RangeRef
{
T& range;
decltype(auto) begin() const
{
return std::begin(range);
}
decltype(auto) end() const
{
return std::end(range);
}
};
template<typename TIterator>
struct RangeRef<std::pair<TIterator, TIterator>>
{
std::pair<TIterator, TIterator> iterators;
TIterator begin() const
{
return iterators.first;
}
TIterator end() const
{
return iterators.second;
}
};
// in case of pass-by-reference
template<typename TIterator>
struct RangeRef<std::pair<TIterator, TIterator>&> : RangeRef<std::pair<TIterator, TIterator>> {};
static_assert(std::is_same_v<decltype(std::declval<RangeRef<std::pair<int*, int*>>>().iterators.first), int*>, "RangeRef for iterator pair not working.");
static_assert(std::is_same_v<decltype(std::declval<RangeRef<std::pair<std::string_view::iterator, std::string_view::iterator>&>>().iterators.first), std::string_view::iterator>, "RangeRef for iterator pair not working.");
template<typename T>
struct is_range_adapter_type : std::false_type {};
template<typename T> requires std::is_base_of_v<RangeAdapter, T>
struct is_range_adapter_type<T> : std::true_type {};
// allow passing in spans and string_views by value
template<typename T, std::size_t Extent>
struct is_range_adapter_type<std::span<T, Extent>> : std::true_type {};
template<typename CharT, typename Traits>
struct is_range_adapter_type<std::basic_string_view<CharT, Traits>> : std::true_type {};
template<typename T>
concept RangeAdapterType = is_range_adapter_type<std::decay_t<T>>::value;
static_assert(!is_range_adapter_type<std::tuple<int, int>>::value);
static_assert(is_range_adapter_type<RangeAdapter>::value);
template<RangeAdapterType T>
struct RangeRef<T>
{
std::remove_reference_t<T> range;
decltype(auto) begin() const
{
return range.begin();
}
decltype(auto) end() const
{
return range.end();
}
};
template<typename TIdx, typename TIterator>
struct EnumeratingIterator
{
TIdx idx;
TIterator base;
EnumeratingIterator(TIdx idx_, TIterator base_) MIJIN_NOEXCEPT : idx(idx_), base(base_) {}
EnumeratingIterator(const EnumeratingIterator&) MIJIN_NOEXCEPT = default;
EnumeratingIterator& operator=(const EnumeratingIterator&) MIJIN_NOEXCEPT = default;
auto operator*() const MIJIN_NOEXCEPT
{
return std::tie(idx, *base);
}
EnumeratingIterator& operator++() MIJIN_NOEXCEPT
{
++idx;
++base;
return *this;
}
EnumeratingIterator operator++(int) MIJIN_NOEXCEPT
{
EnumeratingIterator copy(*this);
++(*this);
return copy;
}
EnumeratingIterator& operator--() MIJIN_NOEXCEPT
{
--idx;
--base;
return *this;
}
EnumeratingIterator operator--(int) MIJIN_NOEXCEPT
{
EnumeratingIterator copy(*this);
--(*this);
return copy;
}
bool operator==(const EnumeratingIterator& other) const MIJIN_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 MIJIN_NOEXCEPT
{
return base != other.base; // note: ignoring idx so we don't have to find it out for end()
}
};
template<typename TIdx, typename TIterator>
EnumeratingIterator(TIdx, TIterator) -> EnumeratingIterator<TIdx, TIterator>;
template<typename TIdx, typename TIterable>
struct Enumeratable : RangeAdapter
{
RangeRef<TIterable> base;
auto begin() const MIJIN_NOEXCEPT
{
return EnumeratingIterator(TIdx(0), base.begin());
}
auto end() const MIJIN_NOEXCEPT
{
return EnumeratingIterator(TIdx(0), base.end());
}
};
template<typename TIdx = std::size_t, typename TIterable>
Enumeratable<TIdx, TIterable> enumerate(TIterable&& iterable)
{
return {.base = {std::forward<TIterable>(iterable)}};
}
template<typename TFirstIterator, typename TSecondIterator>
struct ZippingIterator
{
TFirstIterator first;
TSecondIterator second;
ZippingIterator(TFirstIterator first_, TSecondIterator second_) MIJIN_NOEXCEPT : first(first_), second(second_) {}
ZippingIterator(const ZippingIterator&) MIJIN_NOEXCEPT = default;
ZippingIterator& operator=(const ZippingIterator&) MIJIN_NOEXCEPT = default;
decltype(auto) operator*() const MIJIN_NOEXCEPT
{
return std::tie(*first, *second);
}
ZippingIterator& operator++() MIJIN_NOEXCEPT
{
++first;
++second;
return *this;
}
ZippingIterator operator++(int) MIJIN_NOEXCEPT
{
ZippingIterator copy(*this);
++(*this);
return copy;
}
ZippingIterator& operator--() MIJIN_NOEXCEPT
{
--first;
--second;
return *this;
}
ZippingIterator operator--(int) MIJIN_NOEXCEPT
{
ZippingIterator copy(*this);
--(*this);
return copy;
}
bool operator==(const ZippingIterator& other) const MIJIN_NOEXCEPT
{
return first == other.first || second == other.second; // note: this uses or so reaching the end on one range also ends the zipped one.
}
bool operator!=(const ZippingIterator& other) const MIJIN_NOEXCEPT
{
return !(*this == other);
}
};
template<typename TFirstIterator, typename TSecondIterator>
ZippingIterator(TFirstIterator, TSecondIterator) -> ZippingIterator<TFirstIterator, TSecondIterator>;
template<typename TFirst, typename TSecond>
struct ZippingRange : RangeAdapter
{
RangeRef<TFirst> first;
RangeRef<TSecond> second;
auto begin() const MIJIN_NOEXCEPT
{
return ZippingIterator(first.begin(), second.begin());
}
auto end() const MIJIN_NOEXCEPT
{
return ZippingIterator(first.end(), second.end());
}
};
template<typename TFirst, typename TSecond>
ZippingRange<TFirst, TSecond> zip(TFirst&& first, TSecond&& second)
{
return {.first = {std::forward<TFirst>(first)}, .second = {std::forward<TSecond>(second)}};
}
template<typename TIterator>
struct ReplacingIterator
{
using difference_type = std::ptrdiff_t;
using value_type = typename std::iterator_traits<TIterator>::value_type;
using pointer = std::add_const_t<value_type>*;
using reference = std::add_const_t<value_type>&;
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_) MIJIN_NOEXCEPT : base(base_), what(what_), with(with_) {}
ReplacingIterator(const ReplacingIterator&) MIJIN_NOEXCEPT = default;
ReplacingIterator& operator=(const ReplacingIterator&) MIJIN_NOEXCEPT = default;
pointer operator->() const MIJIN_NOEXCEPT
{
if (*base == what) {
return &with;
}
return &*base;
}
reference operator*() const MIJIN_NOEXCEPT
{
if (*base == what) {
return with;
}
return *base;
}
ReplacingIterator& operator++() MIJIN_NOEXCEPT
{
++base;
return *this;
}
ReplacingIterator operator++(int) MIJIN_NOEXCEPT
{
ReplacingIterator copy(*this);
++(*this);
return copy;
}
ReplacingIterator& operator--() MIJIN_NOEXCEPT
{
--base;
return *this;
}
ReplacingIterator operator--(int) MIJIN_NOEXCEPT
{
ReplacingIterator copy(*this);
--(*this);
return copy;
}
bool operator==(const ReplacingIterator& other) const MIJIN_NOEXCEPT
{
return what == other.what && with == other.with && base == other.base;
}
bool operator!=(const ReplacingIterator& other) const MIJIN_NOEXCEPT
{
return !(*this == other);
}
};
template<typename TIterable>
struct ReplacingRange : RangeAdapter
{
using value_type = typename std::iterator_traits<decltype(std::begin(std::declval<TIterable>()))>::value_type;
RangeRef<TIterable> base;
value_type what;
value_type with;
auto begin() const MIJIN_NOEXCEPT
{
return ReplacingIterator(base.begin(), what, with);
}
auto end() const MIJIN_NOEXCEPT
{
return ReplacingIterator(base.end(), what, with);
}
};
template<typename TIterable>
auto replace(
TIterable&& iterable,
typename std::iterator_traits<decltype(std::begin(std::declval<TIterable>()))>::value_type what,
typename std::iterator_traits<decltype(std::begin(std::declval<TIterable>()))>::value_type with)
{
return ReplacingRange<TIterable>{
.base = {std::forward<TIterable>(iterable)},
.what = std::move(what),
.with = std::move(with)
};
}
template<typename TIterator, typename TFunctor>
struct MappingIterator
{
using orig_value_type = typename std::iterator_traits<TIterator>::value_type;
using difference_type = std::ptrdiff_t;
using value_type = std::invoke_result_t<TFunctor, orig_value_type&>;
using reference = value_type&;
using iterator_category = std::bidirectional_iterator_tag; // TODO?
static_assert(!std::is_same_v<value_type, void>, "Invalid mapping result type.");
TIterator base;
TFunctor functor;
mutable Optional<value_type> result;
MappingIterator(TIterator base_, TFunctor functor_) MIJIN_NOEXCEPT : base(base_), functor(std::move(functor_)) {}
MappingIterator(const MappingIterator&) MIJIN_NOEXCEPT = default;
MappingIterator(MappingIterator&&) MIJIN_NOEXCEPT = default;
MappingIterator& operator=(const MappingIterator&) MIJIN_NOEXCEPT = default;
MappingIterator& operator=(MappingIterator&&) MIJIN_NOEXCEPT = default;
reference operator*() const MIJIN_NOEXCEPT
{
if (result.empty()) {
result = std::invoke(functor, *base);
}
return *result;
}
MappingIterator& operator++() MIJIN_NOEXCEPT
{
++base;
result.reset();
return *this;
}
MappingIterator operator++(int) MIJIN_NOEXCEPT
{
MappingIterator copy(*this);
++(*this);
return copy;
}
MappingIterator& operator--() MIJIN_NOEXCEPT
{
--base;
result.reset();
return *this;
}
MappingIterator operator--(int) MIJIN_NOEXCEPT
{
MappingIterator copy(*this);
--(*this);
return copy;
}
bool operator==(const MappingIterator& other) const MIJIN_NOEXCEPT
{
return base == other.base; // TODO: compare functor? -> doesn't always work and may be useless
}
bool operator!=(const MappingIterator& other) const MIJIN_NOEXCEPT
{
return !(*this == other);
}
};
template<typename TIterable, typename TFunctor>
struct MappingRange : RangeAdapter
{
// using value_type = typename std::iterator_traits<decltype(std::begin(std::declval<TIterable>()))>::value_type;
using value_type = typename std::iterator_traits<decltype(std::declval<RangeRef<TIterable>>().begin())>::value_type;
RangeRef<TIterable> base;
TFunctor functor;
auto begin() const MIJIN_NOEXCEPT
{
return MappingIterator(base.begin(), functor);
}
auto end() const MIJIN_NOEXCEPT
{
return MappingIterator(base.end(), functor);
}
[[nodiscard]] bool empty() const MIJIN_NOEXCEPT {
return base.begin() == base.end();
}
};
template<typename TIterable, typename TFunctor>
auto map(TIterable&& iterable, TFunctor&& functor)
{
return MappingRange<TIterable, TFunctor>{
.base = {std::forward<TIterable>(iterable)},
.functor = std::forward<TFunctor>(functor)
};
}
template<typename TIterator, typename TFunctor>
struct OptionalMappingIterator
{
using orig_value_type = typename std::iterator_traits<TIterator>::value_type;
using difference_type = std::ptrdiff_t;
using optional_type = std::invoke_result_t<TFunctor, orig_value_type>;
using value_type = std::decay_t<decltype(std::declval<optional_type>().value())>;
using iterator_category = std::forward_iterator_tag; // TODO?
using reference = value_type&;
static_assert(!std::is_same_v<value_type, void>, "Invalid mapping result type.");
TIterator base;
TIterator end;
TFunctor functor;
mutable optional_type result; // must be mutable so dereferencing can stay const, not nice :/
OptionalMappingIterator(TIterator base_, TIterator end_, TFunctor functor_) MIJIN_NOEXCEPT : base(base_), end(end_), functor(std::move(functor_)) {
if (base != end)
{
result = functor(*base);
if (!result.has_value()) {
++(*this);
}
}
}
OptionalMappingIterator(const OptionalMappingIterator&) MIJIN_NOEXCEPT = default;
OptionalMappingIterator(OptionalMappingIterator&&) MIJIN_NOEXCEPT = default;
OptionalMappingIterator& operator=(const OptionalMappingIterator&) MIJIN_NOEXCEPT = default;
OptionalMappingIterator& operator=(OptionalMappingIterator&&) MIJIN_NOEXCEPT = default;
reference operator*() const MIJIN_NOEXCEPT
{
return *result;
}
OptionalMappingIterator& operator++() MIJIN_NOEXCEPT
{
do
{
++base;
result = base != end ? functor(*base) : std::nullopt;
} while (base != end && !result.has_value());
return *this;
}
OptionalMappingIterator operator++(int) MIJIN_NOEXCEPT
{
OptionalMappingIterator copy(*this);
++(*this);
return copy;
}
bool operator==(const OptionalMappingIterator& other) const MIJIN_NOEXCEPT
{
return base == other.base && end == other.end; // TODO: compare functor? -> doesn't always work and may be useless
}
bool operator!=(const OptionalMappingIterator& other) const MIJIN_NOEXCEPT
{
return !(*this == other);
}
};
template<typename TIterable, typename TFunctor>
struct OptionalMappingRange : RangeAdapter
{
// using value_type = typename std::iterator_traits<decltype(std::begin(std::declval<TIterable>()))>::value_type;
using value_type = typename std::iterator_traits<decltype(std::declval<RangeRef<TIterable>>().begin())>::value_type;
RangeRef<TIterable> base;
TFunctor functor;
auto begin() const MIJIN_NOEXCEPT
{
return OptionalMappingIterator(base.begin(), base.end(), functor);
}
auto end() const MIJIN_NOEXCEPT
{
return OptionalMappingIterator(base.end(), base.end(), functor);
}
};
template<typename TIterable, typename TFunctor>
auto mapOptional(TIterable&& iterable, TFunctor&& functor)
{
return OptionalMappingRange<TIterable, TFunctor>{
.base = {std::forward<TIterable>(iterable)},
.functor = std::forward<TFunctor>(functor)
};
}
template<typename TIterator, typename TPredicate>
struct FilteringIterator
{
using difference_type = typename std::iterator_traits<TIterator>::difference_type;
using value_type = typename std::iterator_traits<TIterator>::value_type;
using iterator_category = std::forward_iterator_tag; // TODO?
using reference = typename std::iterator_traits<TIterator>::reference;
TIterator base;
TIterator end;
TPredicate predicate;
FilteringIterator(TIterator base_, TIterator end_, TPredicate predicate_) MIJIN_NOEXCEPT : base(base_), end(end_), predicate(std::move(predicate_)) {
if (base != end && !predicate(*base)) {
++(*this);
}
}
FilteringIterator(const FilteringIterator&) MIJIN_NOEXCEPT = default;
FilteringIterator(FilteringIterator&&) MIJIN_NOEXCEPT = default;
FilteringIterator& operator=(const FilteringIterator&) MIJIN_NOEXCEPT = default;
FilteringIterator& operator=(FilteringIterator&&) MIJIN_NOEXCEPT = default;
reference operator*() const MIJIN_NOEXCEPT
{
return *base;
}
FilteringIterator& operator++() MIJIN_NOEXCEPT
{
do
{
++base;
} while (base != end && !predicate(*base));
return *this;
}
FilteringIterator operator++(int) MIJIN_NOEXCEPT
{
FilteringIterator copy(*this);
++(*this);
return copy;
}
bool operator==(const FilteringIterator& other) const MIJIN_NOEXCEPT
{
return base == other.base && end == other.end; // TODO: compare predicate?
}
bool operator!=(const FilteringIterator& other) const MIJIN_NOEXCEPT
{
return !(*this == other);
}
};
template<typename TIterable, typename TPredicate>
struct FilteringRange : RangeAdapter
{
// using value_type = typename std::iterator_traits<decltype(std::begin(std::declval<TIterable>()))>::value_type;
using value_type = typename std::iterator_traits<decltype(std::declval<RangeRef<TIterable>>().begin())>::value_type;
RangeRef<TIterable> base;
TPredicate predicate;
auto begin() const MIJIN_NOEXCEPT
{
return FilteringIterator(base.begin(), base.end(), predicate);
}
auto end() const MIJIN_NOEXCEPT
{
return FilteringIterator(base.end(), base.end(), predicate);
}
};
template<typename TIterable, typename TPredicate>
auto filter(TIterable&& iterable, TPredicate&& predicate)
{
return FilteringRange<TIterable, TPredicate>{
.base = {std::forward<TIterable>(iterable)},
.predicate = std::forward<TPredicate>(predicate)
};
}
template<typename TFirstIterator, typename TSecondIterator>
struct ChainingIterator
{
using difference_type = std::ptrdiff_t;
using value_type = std::remove_reference_t<typename std::iterator_traits<TFirstIterator>::reference>;
using pointer = value_type*;
using reference = value_type&;
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_) MIJIN_NOEXCEPT
: firstBase(firstBase_), firstEnd(firstEnd_), secondBase(secondBase_), secondBegin(secondBegin_) {}
ChainingIterator(const ChainingIterator&) MIJIN_NOEXCEPT = default;
ChainingIterator& operator=(const ChainingIterator&) MIJIN_NOEXCEPT = default;
pointer operator->() const MIJIN_NOEXCEPT
{
if (firstBase == firstEnd)
{
return &*secondBase;
}
return &*firstBase;
}
reference operator*() const MIJIN_NOEXCEPT
{
if (firstBase == firstEnd)
{
return *secondBase;
}
return *firstBase;
}
ChainingIterator& operator++() MIJIN_NOEXCEPT
{
if (firstBase == firstEnd) {
++secondBase;
}
else {
++firstBase;
}
return *this;
}
ChainingIterator operator++(int) MIJIN_NOEXCEPT
{
ChainingIterator copy(*this);
++(*this);
return copy;
}
ChainingIterator& operator--() MIJIN_NOEXCEPT
{
if (secondBase == secondBegin) {
--firstBase;
}
else {
--secondBase;
}
return *this;
}
ChainingIterator operator--(int) MIJIN_NOEXCEPT
{
ChainingIterator copy(*this);
--(*this);
return copy;
}
bool operator==(const ChainingIterator& other) const MIJIN_NOEXCEPT
{
return firstBase == other.firstBase && secondBase == other.secondBase; // should be enough
}
bool operator!=(const ChainingIterator& other) const MIJIN_NOEXCEPT
{
return !(*this == other);
}
};
template<typename TFirstRange, typename TSecondRange>
struct ChainedRange : RangeAdapter
{
using value_type = typename std::iterator_traits<decltype(std::begin(std::declval<TFirstRange>()))>::value_type;
RangeRef<TFirstRange> first;
RangeRef<TSecondRange> second;
auto begin() const MIJIN_NOEXCEPT
{
return ChainingIterator(first.begin(), first.end(), second.begin(), second.begin());
}
auto end() const MIJIN_NOEXCEPT
{
return ChainingIterator(first.end(), first.end(), second.end(), second.begin());
}
};
template<typename TFirstRange, typename TSecondRange>
auto chain(TFirstRange&& firstRange, TSecondRange&& secondRange)
{
return ChainedRange<TFirstRange, TSecondRange>{
.first = {std::forward<TFirstRange>(firstRange)},
.second = {std::forward<TSecondRange>(secondRange)}
};
}
template<typename TFirstRange, typename TSecondRange, typename... TMoreRanges>
auto chain(TFirstRange&& firstRange, TSecondRange&& secondRange, TMoreRanges&&... moreRanges)
{
return chain(std::forward<TFirstRange>(firstRange), chain(std::forward<TSecondRange>(secondRange), std::forward<TMoreRanges>(moreRanges)...));
}
template<typename TType, typename TIterator>
struct TypeFilteringIterator
{
using difference_type = std::ptrdiff_t;
using value_type = TType;
using pointer = std::remove_pointer_t<value_type>*;
using reference = std::remove_reference_t<value_type>&;
using iterator_category = std::bidirectional_iterator_tag; // TODO?
TIterator base;
TIterator end;
TypeFilteringIterator(TIterator base_, TIterator end_) MIJIN_NOEXCEPT : base(base_), end(end_) {
if (base != end && !isCastable()) {
++(*this);
}
}
TypeFilteringIterator(const TypeFilteringIterator&) MIJIN_NOEXCEPT = default;
TypeFilteringIterator(TypeFilteringIterator&&) MIJIN_NOEXCEPT = default;
TypeFilteringIterator& operator=(const TypeFilteringIterator&) MIJIN_NOEXCEPT = default;
TypeFilteringIterator& operator=(TypeFilteringIterator&&) MIJIN_NOEXCEPT = default;
reference operator*() const MIJIN_NOEXCEPT
{
return static_cast<reference>(*base);
}
TypeFilteringIterator& operator++() MIJIN_NOEXCEPT
{
do
{
++base;
} while (base != end && !isCastable());
return *this;
}
TypeFilteringIterator operator++(int) MIJIN_NOEXCEPT
{
FilteringIterator copy(*this);
++(*this);
return copy;
}
bool operator==(const TypeFilteringIterator& other) const MIJIN_NOEXCEPT
{
return base == other.base && end == other.end;
}
bool operator!=(const TypeFilteringIterator& other) const MIJIN_NOEXCEPT
{
return !(*this == other);
}
private:
bool isCastable() const
{
if constexpr (std::is_pointer_v<TType>)
{
return dynamic_cast<TType>(*base);
}
else
{
return dynamic_cast<TType*>(&*base);
}
}
};
template<typename TType, typename TIterable>
struct TypeFilteringRange : RangeAdapter
{
using orig_value_type = typename std::iterator_traits<decltype(std::begin(std::declval<TIterable>()))>::value_type;
using value_type = TType;
RangeRef<TIterable> iterable;
auto begin() const MIJIN_NOEXCEPT
{
return TypeFilteringIterator<TType, decltype(iterable.begin())>(iterable.begin(), iterable.end());
}
auto end() const MIJIN_NOEXCEPT
{
return TypeFilteringIterator<TType, decltype(iterable.begin())>(iterable.end(), iterable.end());
}
};
template<typename TType, typename TIterable>
auto filterType(TIterable&& iterable)
{
return TypeFilteringRange<TType, TIterable>{
.iterable = {std::forward<TIterable>(iterable)}
};
}
template<typename TAs, typename TIterable>
TAs collect(TIterable&& iterable)
{
return TAs(iterable.begin(), iterable.end());
}
namespace pipe
{
template<typename T>
struct Replace
{
T what;
T with;
Replace(T what_, T with_) : what(std::move(what_)), with(std::move(with_)) {}
};
template<typename T>
Replace(T, T) -> Replace<T>;
template<typename TIterable>
auto operator|(TIterable&& iterable, Replace<typename std::iterator_traits<decltype(std::begin(std::declval<TIterable>()))>::value_type> repl)
{
return replace(std::forward<TIterable>(iterable), std::move(repl.what), std::move(repl.with));
}
template<typename T>
struct Collect {};
template<typename TIterable, typename T>
auto operator|(TIterable&& iterable, Collect<T>)
{
return collect<T>(std::forward<TIterable>(iterable));
}
struct Chain {};
template<typename TIterable>
struct Chain2
{
RangeRef<TIterable> range;
};
template<typename TIterable>
auto operator|(TIterable&& iterable, Chain)
{
return Chain2<TIterable>{.range = {.range = std::forward<TIterable>(iterable)}};
}
template<typename TFirstIterable, typename TSecondIterable>
auto operator|(Chain2<TFirstIterable> chain2, TSecondIterable&& secondIterable)
{
return chain(chain2.range.range, std::forward<TSecondIterable>(secondIterable));
}
template<typename T>
struct Map
{
T functor;
explicit Map(T functor_) : functor(std::move(functor_)) {}
};
template<typename T>
Map(T) -> Map<T>;
template<typename TIterable, typename TFunctor>
auto operator|(TIterable&& iterable, Map<TFunctor> mapper)
{
return map(std::forward<TIterable>(iterable), std::move(mapper.functor));
}
template<typename T>
struct MapOptional
{
T functor;
explicit MapOptional(T functor_) : functor(std::move(functor_)) {}
};
template<typename T>
MapOptional(T) -> MapOptional<T>;
template<typename TIterable, typename TFunctor>
auto operator|(TIterable&& iterable, MapOptional<TFunctor> mapper)
{
return mapOptional(std::forward<TIterable>(iterable), std::move(mapper.functor));
}
template<typename T>
struct Filter
{
T predicate;
explicit Filter(T predicate_) : predicate(std::move(predicate_)) {}
};
template<typename T>
Filter(T) -> Filter<T>;
template<typename TIterable, typename TFilter>
auto operator|(TIterable&& iterable, Filter<TFilter> filterer)
{
return filter(std::forward<TIterable>(iterable), std::move(filterer.predicate));
}
template<typename T>
struct FilterType
{
};
template<typename TIterable, typename T>
auto operator|(TIterable&& iterable, FilterType<T>)
{
return filterType<T>(std::forward<TIterable>(iterable));
}
template<std::size_t idx>
struct Xth {};
using First = Xth<0>;
using Second = Xth<1>;
template<typename TIterable, std::size_t idx>
auto operator|(TIterable&& iterable, Xth<idx>)
{
return map(std::forward<TIterable>(iterable), [](auto&& element) { return std::get<idx>(element); } );
}
}
} // namespace mijin
#endif // MIJIN_UTIL_ITERATORS_HPP_INCLUDED