Loading...
Searching...
No Matches
take_exactly_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 <span>
#include <type_traits>
#include <seqan3/core/detail/iterator_traits.hpp>
#include <seqan3/core/detail/template_inspection.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_exactly
// ============================================================================
template <std::ranges::view urng_t, bool or_throw>
class view_take_exactly : public std::ranges::view_interface<view_take_exactly<urng_t, or_throw>>
{
private:
urng_t urange;
size_t target_size;
template <bool const_range>
class basic_iterator;
private:
using iterator = basic_iterator<false>;
using const_iterator = basic_iterator<true>;
public:
view_take_exactly() = default;
view_take_exactly(view_take_exactly const & rhs) = default;
view_take_exactly(view_take_exactly && rhs) = default;
view_take_exactly & operator=(view_take_exactly const & rhs) = default;
view_take_exactly & operator=(view_take_exactly && rhs) = default;
~view_take_exactly() = default;
constexpr view_take_exactly(urng_t _urange, size_t const _size) : urange{std::move(_urange)}, target_size{_size}
{
if constexpr (std::ranges::sized_range<urng_t>)
{
if (std::ranges::size(urange) < target_size)
{
if constexpr (or_throw)
{
throw std::invalid_argument{
"You are trying to construct a detail::take_exactly_or_throw from a range that is strictly "
"smaller."};
}
else
{
target_size = std::ranges::size(urange);
}
}
}
}
template <std::ranges::viewable_range rng_t>
requires std::constructible_from<rng_t, std::views::all_t<rng_t>>
constexpr view_take_exactly(rng_t && _urange, size_t const _size) :
view_take_exactly{std::views::all(std::forward<rng_t>(_urange)), _size}
{}
constexpr auto begin() noexcept
{
if constexpr (std::ranges::random_access_range<urng_t> && std::ranges::sized_range<urng_t>)
return std::ranges::begin(urange);
else
return iterator{std::ranges::begin(urange), 0, target_size, this};
}
constexpr auto begin() const noexcept
requires const_iterable_range<urng_t> && std::ranges::forward_range<urng_t>
{
if constexpr (std::ranges::random_access_range<urng_t> && std::ranges::sized_range<urng_t>)
return std::ranges::cbegin(urange);
else
{
// const_iterator does not work if the underlying iterator is a std::input_iterator (it needs to access
// target_size)
return const_iterator{std::ranges::begin(urange), 0, target_size};
}
}
constexpr auto end() noexcept
{
if constexpr (std::ranges::random_access_range<urng_t> && std::ranges::sized_range<urng_t>)
return std::ranges::begin(urange) + target_size;
else
return std::ranges::end(urange);
}
constexpr auto end() const noexcept
requires const_iterable_range<urng_t> && std::ranges::forward_range<urng_t>
{
if constexpr (std::ranges::random_access_range<urng_t> && std::ranges::sized_range<urng_t>)
return std::ranges::cbegin(urange) + target_size;
else
return std::ranges::cend(urange);
}
constexpr auto size() const noexcept
{
return target_size;
}
};
template <typename urng_t, bool or_throw = false>
view_take_exactly(urng_t &&, size_t) -> view_take_exactly<std::views::all_t<urng_t>, or_throw>;
template <std::ranges::view urng_t, bool or_throw>
template <bool const_range>
class view_take_exactly<urng_t, or_throw>::basic_iterator :
public inherited_iterator_base<basic_iterator<const_range>, maybe_const_iterator_t<const_range, urng_t>>
{
private:
using base_base_t = maybe_const_iterator_t<const_range, urng_t>;
using base_t = inherited_iterator_base<basic_iterator, maybe_const_iterator_t<const_range, urng_t>>;
using sentinel_type = maybe_const_sentinel_t<const_range, urng_t>;
size_t pos{};
size_t max_pos{};
std::conditional_t<!std::forward_iterator<base_base_t>, view_take_exactly *, detail::ignore_t> host_ptr;
public:
basic_iterator() = default;
basic_iterator(basic_iterator const & rhs) = default;
basic_iterator(basic_iterator && rhs) = default;
basic_iterator & operator=(basic_iterator const & rhs) = default;
basic_iterator & operator=(basic_iterator && rhs) = default;
~basic_iterator() = default;
constexpr basic_iterator(base_base_t it) noexcept(noexcept(base_t{it})) : base_t{std::move(it)}
{}
constexpr basic_iterator(base_base_t it,
size_t const _pos,
size_t const _max_pos,
view_take_exactly * host = nullptr) noexcept(noexcept(base_t{it})) :
base_t{std::move(it)},
pos{_pos},
max_pos(_max_pos)
{
host_ptr = host;
if constexpr (!std::forward_iterator<base_base_t>)
{
assert(host_ptr != nullptr);
}
}
using typename base_t::difference_type;
using typename base_t::reference;
constexpr basic_iterator & operator++() noexcept(noexcept(++std::declval<base_t &>()))
{
base_t::operator++();
++pos;
if constexpr (!std::forward_iterator<base_base_t>)
--host_ptr->target_size;
return *this;
}
constexpr decltype(auto) operator++(int) noexcept(noexcept(++std::declval<basic_iterator &>())
&& (std::same_as<decltype(std::declval<base_base_t &>()++), void>
|| std::is_nothrow_copy_constructible_v<basic_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<base_base_t &>()++), void>)
{
++(*this);
}
else
{
basic_iterator cpy{*this};
++(*this);
return cpy;
}
}
constexpr basic_iterator & operator--() noexcept(noexcept(--std::declval<base_base_t &>()))
requires std::bidirectional_iterator<base_base_t>
{
base_t::operator--();
--pos;
return *this;
}
constexpr basic_iterator operator--(int)
noexcept(noexcept(--std::declval<basic_iterator &>()) && std::is_nothrow_copy_constructible_v<basic_iterator>)
requires std::bidirectional_iterator<base_base_t>
{
basic_iterator cpy{*this};
--(*this);
return cpy;
}
constexpr basic_iterator & operator+=(difference_type const skip)
noexcept(noexcept(std::declval<base_t &>() += skip))
requires std::random_access_iterator<base_base_t>
{
base_t::operator+=(skip);
pos += skip;
return *this;
}
constexpr basic_iterator & operator-=(difference_type const skip)
noexcept(noexcept(std::declval<base_t &>() -= skip))
requires std::random_access_iterator<base_base_t>
{
base_t::operator-=(skip);
pos -= skip;
return *this;
}
constexpr bool operator==(basic_iterator const & rhs) const
noexcept(!or_throw && noexcept(std::declval<base_base_t &>() == std::declval<base_base_t &>()))
requires std::forward_iterator<base_base_t>
{
return this->base() == rhs.base();
}
constexpr bool operator==(sentinel_type const & rhs) const
noexcept(!or_throw && noexcept(std::declval<base_base_t const &>() == std::declval<sentinel_type const &>()))
{
if (pos >= max_pos)
return true;
if (this->base() == rhs)
{
if constexpr (or_throw)
throw unexpected_end_of_input{"Reached end of input before designated size."};
return true;
}
else
{
return false;
}
}
constexpr friend bool operator==(sentinel_type const & lhs, basic_iterator const & rhs)
noexcept(noexcept(rhs == lhs))
{
return rhs == lhs;
}
constexpr bool operator!=(sentinel_type const & rhs) const
noexcept(noexcept(std::declval<basic_iterator &>() == rhs))
{
return !(*this == rhs);
}
constexpr bool operator!=(basic_iterator const & rhs) const
noexcept(noexcept(std::declval<basic_iterator &>() == rhs))
requires std::forward_iterator<base_base_t>
{
return !(*this == rhs);
}
constexpr friend bool operator!=(sentinel_type const & lhs, basic_iterator const & rhs)
noexcept(noexcept(rhs != lhs))
{
return rhs != lhs;
}
constexpr reference operator[](std::make_unsigned_t<difference_type> const n) const
noexcept(noexcept(std::declval<base_base_t &>()[0]))
requires std::random_access_iterator<base_base_t>
{
return base_t::operator[](n);
}
};
// ============================================================================
// take_fn (adaptor definition)
// ============================================================================
template <bool or_throw>
struct take_exactly_fn
{
constexpr auto operator()(size_t const size) const
{
return adaptor_from_functor{*this, size};
}
template <std::ranges::range urng_t>
constexpr auto operator()(urng_t && urange, size_t target_size) const
{
static_assert(std::ranges::viewable_range<urng_t>,
"The views::take adaptor can only be passed viewable_ranges, i.e. Views or &-to-non-View.");
// safeguard against wrong size
if constexpr (std::ranges::sized_range<urng_t>)
{
if constexpr (or_throw)
{
if (target_size > std::ranges::size(urange))
{
throw std::invalid_argument{"You are trying to construct a detail::take_exactly_or_throw from a "
"range that is strictly smaller."};
}
}
else
{
target_size = std::min<size_t>(target_size, std::ranges::size(urange));
}
}
// string_view
if constexpr (is_type_specialisation_of_v<std::remove_cvref_t<urng_t>, std::basic_string_view>)
{
// in standard
return urange.substr(0, target_size);
}
// string const &
else if constexpr (is_type_specialisation_of_v<std::remove_cvref_t<urng_t>, std::basic_string>
&& std::is_const_v<std::remove_reference_t<urng_t>>)
{
// not in standard
// seqan3::views::type_reduce does this too
return std::basic_string_view{std::ranges::data(urange), target_size};
}
// contiguous
else if constexpr (std::ranges::borrowed_range<urng_t> && std::ranges::contiguous_range<urng_t>
&& std::ranges::sized_range<urng_t>)
{
// not in standard (special case for std::span in standard)
// seqan3::views::type_reduce does this too
return std::span{std::ranges::data(urange), target_size};
}
// random_access
else if constexpr (std::ranges::borrowed_range<urng_t> && std::ranges::random_access_range<urng_t>
&& std::ranges::sized_range<urng_t>)
{
// not in standard
// seqan3::views::type_reduce does this too
return std::ranges::subrange<std::ranges::iterator_t<urng_t>, std::ranges::iterator_t<urng_t>>{
std::ranges::begin(urange),
std::ranges::begin(urange) + target_size,
target_size};
}
// our type
else
{
return view_take_exactly<std::views::all_t<urng_t>, or_throw>{std::forward<urng_t>(urange), target_size};
}
}
};
} // namespace seqan3::detail
// ============================================================================
// detail::take_exactly (adaptor instance definition)
// ============================================================================
namespace seqan3::detail
{
inline constexpr auto take_exactly = take_exactly_fn<false>{};
// ============================================================================
// detail::take_exactly_or_throw (adaptor instance definition)
// ============================================================================
inline constexpr auto take_exactly_or_throw = take_exactly_fn<true>{};
} // namespace seqan3::detail
namespace std::ranges
{
template <std::ranges::view urng_t, bool or_throw>
inline constexpr bool enable_borrowed_range<seqan3::detail::view_take_exactly<urng_t, or_throw>> =
enable_borrowed_range<urng_t>;
} // namespace std::ranges
adaptor_from_functor.hpp
Provides seqan3::detail::adaptor_from_functor.
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)
seqan3::pack_traits::size
constexpr size_t size
The size of a type pack.
Definition type_pack/traits.hpp:143
inherited_iterator_base.hpp
Provides the seqan3::detail::inherited_iterator_base template.
exception.hpp
Provides exceptions used in the I/O module.
std::is_const_v
T is_const_v
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::min
T min(T... args)
std::ranges
[adaptor_def]
Definition take_exactly_view.hpp:592
std::rel_ops::operator!=
T operator!=(T... args)
template_inspection.hpp
Provides type traits for working with templates.
transformation_trait_or.hpp
Provides seqan3::detail::transformation_trait_or.
type_traits
concept.hpp
Additional non-standard concepts for ranges.
Hide me