Loading...
Searching...
No Matches
take_until_view.hpp
Go to the documentation of this file.
// SPDX-FileCopyrightText: 2006-2025 Knut Reinert & Freie Universität Berlin
// SPDX-FileCopyrightText: 2016-2025 Knut Reinert & MPI für molekulare Genetik
// SPDX-License-Identifier: BSD-3-Clause
#pragma once
#include <algorithm>
#include <concepts>
#include <iterator>
#include <ranges>
#include <type_traits>
#include <seqan3/contrib/std/detail/movable_box.hpp>
#include <seqan3/core/detail/iterator_traits.hpp>
#include <seqan3/core/range/detail/adaptor_from_functor.hpp>
#include <seqan3/core/range/detail/inherited_iterator_base.hpp>
#include <seqan3/core/range/type_traits.hpp>
#include <seqan3/io/exception.hpp>
#include <seqan3/utility/range/concept.hpp>
#include <seqan3/utility/type_traits/detail/transformation_trait_or.hpp>
namespace seqan3::detail
{
// ============================================================================
// view_take_until
// ============================================================================
template <std::ranges::view urng_t, typename fun_t, bool or_throw, bool and_consume>
class view_take_until : public std::ranges::view_interface<view_take_until<urng_t, fun_t, or_throw, and_consume>>
{
private:
static_assert(std::invocable<fun_t, std::ranges::range_reference_t<urng_t>>,
"The functor type for detail::take_until must model"
"std::invocable<fun_t, std::ranges::range_reference_t<urng_t>>.");
static_assert(std::convertible_to<std::invoke_result_t<fun_t &&, std::ranges::range_reference_t<urng_t>>, bool>,
"The result type of the functor for detail::take_until must be a boolean.");
urng_t urange;
seqan::stl::detail::movable_box_t<fun_t> fun;
static constexpr bool const_iterable =
const_iterable_range<urng_t> && indirect_unary_predicate_on_range<fun_t const, urng_t const>;
template <bool const_range>
using basic_iterator = seqan3::detail::maybe_const_iterator_t<const_range, urng_t>;
template <bool const_range>
class basic_sentinel;
template <bool const_range>
class basic_consume_iterator;
template <bool const_range>
using basic_consume_sentinel = std::default_sentinel_t;
public:
view_take_until() = default;
constexpr view_take_until(view_take_until const & rhs) = default;
constexpr view_take_until(view_take_until && rhs) = default;
constexpr view_take_until & operator=(view_take_until const & rhs) = default;
constexpr view_take_until & operator=(view_take_until && rhs) = default;
~view_take_until() = default;
view_take_until(urng_t && _urange, fun_t && _fun) :
urange{std::forward<urng_t>(_urange)},
fun{std::forward<fun_t>(_fun)}
{}
template <std::ranges::viewable_range rng_t>
requires std::constructible_from<urng_t, std::views::all_t<rng_t>>
view_take_until(rng_t && _urange, fun_t && _fun) :
view_take_until{std::views::all(std::forward<rng_t>(_urange)), std::forward<fun_t>(_fun)}
{}
auto begin() noexcept
{
if constexpr (and_consume && !std::ranges::forward_range<urng_t>)
return basic_consume_iterator<false>{std::ranges::begin(urange), fun, std::ranges::end(urange)};
else
return basic_iterator<false>{std::ranges::begin(urange)};
}
auto begin() const noexcept
requires const_iterable
{
if constexpr (and_consume && !std::ranges::forward_range<urng_t const>)
return basic_consume_iterator<true>{std::ranges::cbegin(urange), fun, std::ranges::cend(urange)};
else
return basic_iterator<true>{std::ranges::begin(urange)};
}
auto end() noexcept
{
if constexpr (and_consume && !std::ranges::forward_range<urng_t>)
return basic_consume_sentinel<false>{};
else
return basic_sentinel<false>{std::ranges::end(urange), fun};
}
auto end() const noexcept
requires const_iterable
{
if constexpr (and_consume && !std::ranges::forward_range<urng_t const>)
return basic_consume_sentinel<true>{};
else
return basic_sentinel<true>{std::ranges::end(urange), fun};
}
};
template <typename urng_t, typename fun_t, bool or_throw = false, bool and_consume = false>
view_take_until(urng_t &&, fun_t &&) -> view_take_until<std::views::all_t<urng_t>, fun_t, or_throw, and_consume>;
template <std::ranges::view urng_t, typename fun_t, bool or_throw, bool and_consume>
template <bool const_range>
class view_take_until<urng_t, fun_t, or_throw, and_consume>::basic_consume_iterator :
public inherited_iterator_base<basic_consume_iterator<const_range>,
seqan3::detail::maybe_const_iterator_t<const_range, urng_t>>
{
private:
using underlying_iterator_t = seqan3::detail::maybe_const_iterator_t<const_range, urng_t>;
using base_t = inherited_iterator_base<basic_consume_iterator, underlying_iterator_t>;
seqan::stl::detail::movable_box_t<fun_t> const * fun{nullptr};
using underlying_sentinel_t = seqan3::detail::maybe_const_sentinel_t<const_range, urng_t>;
underlying_sentinel_t underlying_sentinel;
bool at_end_gracefully = false;
public:
constexpr basic_consume_iterator() = default;
constexpr basic_consume_iterator(basic_consume_iterator const & rhs) = default;
constexpr basic_consume_iterator(basic_consume_iterator && rhs) = default;
constexpr basic_consume_iterator & operator=(basic_consume_iterator const & rhs) = default;
constexpr basic_consume_iterator & operator=(basic_consume_iterator && rhs) = default;
~basic_consume_iterator() = default;
basic_consume_iterator(underlying_iterator_t it,
seqan::stl::detail::movable_box_t<fun_t> const & _fun,
underlying_sentinel_t sen) noexcept(noexcept(base_t{it})) :
base_t{std::move(it)},
fun{std::addressof(_fun)},
underlying_sentinel{std::move(sen)}
{
if ((this->base() != underlying_sentinel) && fun->operator()(**this))
{
at_end_gracefully = true;
++(*this);
}
}
using difference_type = std::iter_difference_t<underlying_iterator_t>;
using value_type = std::iter_value_t<underlying_iterator_t>;
using reference = std::iter_reference_t<underlying_iterator_t>;
using pointer = detail::iter_pointer_t<underlying_iterator_t>;
using iterator_category = std::input_iterator_tag;
basic_consume_iterator & operator++()
noexcept(noexcept(++std::declval<base_t &>())
&& noexcept(std::declval<underlying_iterator_t &>() != std::declval<underlying_sentinel_t &>())
&& noexcept(fun->operator()(std::declval<reference>())))
{
base_t::operator++();
while ((this->base() != underlying_sentinel) && fun->operator()(**this))
{
at_end_gracefully = true;
base_t::operator++();
}
return *this;
}
decltype(auto) operator++(int) noexcept(noexcept(++std::declval<basic_consume_iterator &>())
&& (std::same_as<decltype(std::declval<underlying_iterator_t &>()++), void>
|| std::is_nothrow_copy_constructible_v<basic_consume_iterator>))
{
// if underlying iterator is a C++20 input iterator (i.e. returns void), return void too.
if constexpr (std::same_as<decltype(std::declval<underlying_iterator_t &>()++), void>)
{
++(*this);
}
else
{
basic_consume_iterator cpy{*this};
++(*this);
return cpy;
}
}
bool operator==(basic_consume_sentinel<const_range> const &) const
noexcept(!or_throw
&& noexcept(std::declval<underlying_iterator_t &>() != std::declval<underlying_sentinel_t &>())
&& noexcept(fun->operator()(std::declval<reference>())))
{
if (at_end_gracefully)
return true;
if (this->base() == underlying_sentinel)
{
if constexpr (or_throw)
throw unexpected_end_of_input{"Reached end of input before functor evaluated to true."};
else
return true;
}
return fun->operator()(**this);
}
friend bool operator==(basic_consume_sentinel<const_range> const & lhs, basic_consume_iterator const & rhs)
noexcept(noexcept(rhs == lhs))
{
return rhs == lhs;
}
bool operator!=(basic_consume_sentinel<const_range> const & rhs) const
noexcept(noexcept(std::declval<basic_consume_iterator &>() == rhs))
{
return !(*this == rhs);
}
friend bool operator!=(basic_consume_sentinel<const_range> const & lhs, basic_consume_iterator const & rhs)
noexcept(noexcept(rhs != lhs))
{
return rhs != lhs;
}
};
template <std::ranges::view urng_t, typename fun_t, bool or_throw, bool and_consume>
template <bool const_range>
class view_take_until<urng_t, fun_t, or_throw, and_consume>::basic_sentinel
{
private:
using underlying_sentinel_t = seqan3::detail::maybe_const_sentinel_t<const_range, urng_t>;
underlying_sentinel_t underlying_sentinel{};
seqan::stl::detail::movable_box_t<fun_t> const * fun{nullptr};
public:
basic_sentinel() = default;
basic_sentinel(basic_sentinel const &) = default;
basic_sentinel(basic_sentinel &&) = default;
basic_sentinel & operator=(basic_sentinel const &) = default;
basic_sentinel & operator=(basic_sentinel &&) = default;
~basic_sentinel() = default;
explicit basic_sentinel(underlying_sentinel_t underlying_sentinel,
seqan::stl::detail::movable_box_t<fun_t> const & _fun) :
underlying_sentinel{std::move(underlying_sentinel)},
fun{std::addressof(_fun)}
{}
basic_sentinel(basic_sentinel<!const_range> other)
requires const_range && std::convertible_to<std::ranges::sentinel_t<urng_t>, underlying_sentinel_t>
: underlying_sentinel{std::move(other.underlying_sentinel)}, fun{other.fun}
{}
friend bool operator==(basic_iterator<const_range> const & lhs, basic_sentinel const & rhs)
{
// Actual comparison delegated to lhs base
if (lhs == rhs.underlying_sentinel)
{
if constexpr (or_throw)
throw unexpected_end_of_input{"Reached end of input before functor evaluated to true."};
else
return true;
}
return rhs.fun->operator()(*lhs);
}
friend bool operator==(basic_sentinel const & lhs, basic_iterator<const_range> const & rhs)
{
return rhs == lhs;
}
friend bool operator!=(basic_iterator<const_range> const & lhs, basic_sentinel const & rhs)
{
return !(lhs == rhs);
}
friend bool operator!=(basic_sentinel const & lhs, basic_iterator<const_range> const & rhs)
{
return rhs != lhs;
}
template <bool other_const_range = !const_range>
requires (std::sentinel_for<underlying_sentinel_t, basic_iterator<other_const_range>>)
friend bool operator==(basic_iterator<other_const_range> const & lhs, basic_sentinel const & rhs)
{
// Actual comparison delegated to lhs base
if (lhs == rhs.underlying_sentinel)
{
if constexpr (or_throw)
throw unexpected_end_of_input{"Reached end of input before functor evaluated to true."};
else
return true;
}
return rhs.fun->operator()(*lhs);
}
template <bool other_const_range = !const_range>
requires (std::sentinel_for<underlying_sentinel_t, basic_iterator<other_const_range>>)
friend bool operator==(basic_sentinel const & lhs, basic_iterator<other_const_range> const & rhs)
{
return rhs == lhs;
}
template <bool other_const_range = !const_range>
requires (std::sentinel_for<underlying_sentinel_t, basic_iterator<other_const_range>>)
friend bool operator!=(basic_iterator<other_const_range> const & lhs, basic_sentinel const & rhs)
{
return !(lhs == rhs);
}
template <bool other_const_range = !const_range>
requires (std::sentinel_for<underlying_sentinel_t, basic_iterator<other_const_range>>)
friend bool operator!=(basic_sentinel const & lhs, basic_iterator<other_const_range> const & rhs)
{
return rhs != lhs;
}
};
// ============================================================================
// take_until_fn (adaptor definition)
// ============================================================================
template <bool or_throw, bool and_consume>
struct take_until_fn
{
template <typename fun_t>
constexpr auto operator()(fun_t && fun) const
{
return adaptor_from_functor{*this, std::forward<fun_t>(fun)};
}
template <std::ranges::viewable_range urng_t, typename fun_t>
constexpr auto operator()(urng_t && urange, fun_t && fun) const
{
return view_take_until<std::views::all_t<urng_t>, fun_t, or_throw, and_consume>{
std::views::all(std::forward<urng_t>(urange)),
std::forward<fun_t>(fun)};
}
};
} // namespace seqan3::detail
// ============================================================================
// detail::take_until (adaptor instance definition)
// ============================================================================
namespace seqan3::detail
{
inline constexpr auto take_until = take_until_fn<false, false>{};
// ============================================================================
// detail::take_until_or_throw (adaptor instance definition)
// ============================================================================
inline constexpr auto take_until_or_throw = take_until_fn<true, false>{};
// ============================================================================
// detail::take_until_and_consume (adaptor instance definition)
// ============================================================================
inline constexpr auto take_until_and_consume = take_until_fn<false, true>{};
// ============================================================================
// detail::take_until_or_throw_and_consume (adaptor instance definition)
// ============================================================================
inline constexpr auto take_until_or_throw_and_consume = take_until_fn<true, true>{};
} // namespace seqan3::detail
namespace std::ranges
{
template <std::ranges::view urng_t, typename fun_t, bool or_throw, bool and_consume>
inline constexpr bool enable_borrowed_range<seqan3::detail::view_take_until<urng_t, fun_t, or_throw, and_consume>> =
enable_borrowed_range<urng_t>;
} // namespace std::ranges
adaptor_from_functor.hpp
Provides seqan3::detail::adaptor_from_functor.
std::addressof
T addressof(T... args)
algorithm
type_traits.hpp
Provides various transformation traits used by the range module.
std::declval
T declval(T... args)
std::forward
T forward(T... args)
inherited_iterator_base.hpp
Provides the seqan3::detail::inherited_iterator_base template.
exception.hpp
Provides exceptions used in the I/O module.
std::is_nothrow_copy_constructible_v
T is_nothrow_copy_constructible_v
iterator_traits.hpp
Provides various transformation traits for use on iterators.
iterator
std::ranges
[adaptor_def]
Definition take_exactly_view.hpp:592
std::rel_ops::operator!=
T operator!=(T... args)
transformation_trait_or.hpp
Provides seqan3::detail::transformation_trait_or.
type_traits
concept.hpp
Additional non-standard concepts for ranges.
Hide me