Added filter() and some more iterator utilities.
This commit is contained in:
parent
2faae0e5ee
commit
cff5879572
@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <optional>
|
||||||
#include <span>
|
#include <span>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
@ -30,6 +31,29 @@ struct RangeRef
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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>
|
template<typename T>
|
||||||
struct is_range_adapter_type : std::false_type {};
|
struct is_range_adapter_type : std::false_type {};
|
||||||
|
|
||||||
@ -199,7 +223,7 @@ struct ReplacingIterator
|
|||||||
|
|
||||||
ReplacingIterator operator--(int) noexcept
|
ReplacingIterator operator--(int) noexcept
|
||||||
{
|
{
|
||||||
EnumeratingIterator copy(*this);
|
ReplacingIterator copy(*this);
|
||||||
--(*this);
|
--(*this);
|
||||||
return copy;
|
return copy;
|
||||||
}
|
}
|
||||||
@ -254,10 +278,14 @@ struct MappingIterator
|
|||||||
using orig_value_type = typename std::iterator_traits<TIterator>::value_type;
|
using orig_value_type = typename std::iterator_traits<TIterator>::value_type;
|
||||||
using difference_type = std::ptrdiff_t;
|
using difference_type = std::ptrdiff_t;
|
||||||
using value_type = std::invoke_result_t<TFunctor, orig_value_type>;
|
using value_type = std::invoke_result_t<TFunctor, orig_value_type>;
|
||||||
|
using reference = value_type&;
|
||||||
using iterator_category = std::bidirectional_iterator_tag; // TODO?
|
using iterator_category = std::bidirectional_iterator_tag; // TODO?
|
||||||
|
|
||||||
|
static_assert(!std::is_same_v<value_type, void>, "Invalid mapping result type.");
|
||||||
|
|
||||||
TIterator base;
|
TIterator base;
|
||||||
TFunctor functor;
|
TFunctor functor;
|
||||||
|
mutable std::optional<value_type> result;
|
||||||
|
|
||||||
MappingIterator(TIterator base_, TFunctor functor_) noexcept : base(base_), functor(std::move(functor_)) {}
|
MappingIterator(TIterator base_, TFunctor functor_) noexcept : base(base_), functor(std::move(functor_)) {}
|
||||||
MappingIterator(const MappingIterator&) noexcept = default;
|
MappingIterator(const MappingIterator&) noexcept = default;
|
||||||
@ -266,20 +294,24 @@ struct MappingIterator
|
|||||||
MappingIterator& operator=(const MappingIterator&) noexcept = default;
|
MappingIterator& operator=(const MappingIterator&) noexcept = default;
|
||||||
MappingIterator& operator=(MappingIterator&&) noexcept = default;
|
MappingIterator& operator=(MappingIterator&&) noexcept = default;
|
||||||
|
|
||||||
value_type operator*() const noexcept
|
reference operator*() const noexcept
|
||||||
{
|
{
|
||||||
return functor(*base);
|
if (!result.has_value()) {
|
||||||
|
result = functor(*base);
|
||||||
|
}
|
||||||
|
return *result;
|
||||||
}
|
}
|
||||||
|
|
||||||
MappingIterator& operator++() noexcept
|
MappingIterator& operator++() noexcept
|
||||||
{
|
{
|
||||||
++base;
|
++base;
|
||||||
|
result = std::nullopt;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
MappingIterator operator++(int) noexcept
|
MappingIterator operator++(int) noexcept
|
||||||
{
|
{
|
||||||
ReplacingIterator copy(*this);
|
MappingIterator copy(*this);
|
||||||
++(*this);
|
++(*this);
|
||||||
return copy;
|
return copy;
|
||||||
}
|
}
|
||||||
@ -287,19 +319,20 @@ struct MappingIterator
|
|||||||
MappingIterator& operator--() noexcept
|
MappingIterator& operator--() noexcept
|
||||||
{
|
{
|
||||||
--base;
|
--base;
|
||||||
|
result = std::nullopt;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
MappingIterator operator--(int) noexcept
|
MappingIterator operator--(int) noexcept
|
||||||
{
|
{
|
||||||
EnumeratingIterator copy(*this);
|
MappingIterator copy(*this);
|
||||||
--(*this);
|
--(*this);
|
||||||
return copy;
|
return copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator==(const MappingIterator& other) const noexcept
|
bool operator==(const MappingIterator& other) const noexcept
|
||||||
{
|
{
|
||||||
return functor == other.functor && base == other.base;
|
return base == other.base; // TODO: compare functor? -> doesn't always work and may be useless
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator!=(const MappingIterator& other) const noexcept
|
bool operator!=(const MappingIterator& other) const noexcept
|
||||||
@ -311,7 +344,8 @@ struct MappingIterator
|
|||||||
template<typename TIterable, typename TFunctor>
|
template<typename TIterable, typename TFunctor>
|
||||||
struct MappingRange : RangeAdapter
|
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::begin(std::declval<TIterable>()))>::value_type;
|
||||||
|
using value_type = typename std::iterator_traits<decltype(std::declval<RangeRef<TIterable>>().begin())>::value_type;
|
||||||
|
|
||||||
RangeRef<TIterable> base;
|
RangeRef<TIterable> base;
|
||||||
TFunctor functor;
|
TFunctor functor;
|
||||||
@ -336,6 +370,184 @@ auto map(TIterable&& iterable, 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_) noexcept : base(base_), end(end_), functor(std::move(functor_)) {
|
||||||
|
if (base != end)
|
||||||
|
{
|
||||||
|
result = functor(*base);
|
||||||
|
if (!result.has_value()) {
|
||||||
|
++(*this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OptionalMappingIterator(const OptionalMappingIterator&) noexcept = default;
|
||||||
|
OptionalMappingIterator(OptionalMappingIterator&&) noexcept = default;
|
||||||
|
|
||||||
|
OptionalMappingIterator& operator=(const OptionalMappingIterator&) noexcept = default;
|
||||||
|
OptionalMappingIterator& operator=(OptionalMappingIterator&&) noexcept = default;
|
||||||
|
|
||||||
|
reference operator*() const noexcept
|
||||||
|
{
|
||||||
|
return *result;
|
||||||
|
}
|
||||||
|
|
||||||
|
OptionalMappingIterator& operator++() noexcept
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
++base;
|
||||||
|
result = base != end ? functor(*base) : std::nullopt;
|
||||||
|
} while (base != end && !result.has_value());
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
OptionalMappingIterator operator++(int) noexcept
|
||||||
|
{
|
||||||
|
OptionalMappingIterator copy(*this);
|
||||||
|
++(*this);
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const OptionalMappingIterator& other) const 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 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 noexcept
|
||||||
|
{
|
||||||
|
return OptionalMappingIterator(base.begin(), base.end(), functor);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto end() const 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_) noexcept : base(base_), end(end_), predicate(std::move(predicate_)) {
|
||||||
|
if (base != end && !predicate(*base)) {
|
||||||
|
++(*this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FilteringIterator(const FilteringIterator&) noexcept = default;
|
||||||
|
FilteringIterator(FilteringIterator&&) noexcept = default;
|
||||||
|
|
||||||
|
FilteringIterator& operator=(const FilteringIterator&) noexcept = default;
|
||||||
|
FilteringIterator& operator=(FilteringIterator&&) noexcept = default;
|
||||||
|
|
||||||
|
reference operator*() const noexcept
|
||||||
|
{
|
||||||
|
return *base;
|
||||||
|
}
|
||||||
|
|
||||||
|
FilteringIterator& operator++() noexcept
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
++base;
|
||||||
|
} while (base != end && !predicate(*base));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
FilteringIterator operator++(int) noexcept
|
||||||
|
{
|
||||||
|
FilteringIterator copy(*this);
|
||||||
|
++(*this);
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const FilteringIterator& other) const noexcept
|
||||||
|
{
|
||||||
|
return base == other.base && end == other.end; // TODO: compare predicate?
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const FilteringIterator& other) const 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 noexcept
|
||||||
|
{
|
||||||
|
return FilteringIterator(base.begin(), base.end(), predicate);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto end() const 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>
|
template<typename TFirstIterator, typename TSecondIterator>
|
||||||
struct ChainingIterator
|
struct ChainingIterator
|
||||||
{
|
{
|
||||||
@ -524,6 +736,50 @@ auto operator|(TIterable&& iterable, Map<TFunctor> mapper)
|
|||||||
{
|
{
|
||||||
return map(std::forward<TIterable>(iterable), std::move(mapper.functor));
|
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<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
|
} // namespace mijin
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user