SeqAn3  3.2.0-rc.1
The Modern C++ library for sequence analysis.
take_exactly_view.hpp
Go to the documentation of this file.
1 // -----------------------------------------------------------------------------------------------------
2 // Copyright (c) 2006-2022, Knut Reinert & Freie Universität Berlin
3 // Copyright (c) 2016-2022, Knut Reinert & MPI für molekulare Genetik
4 // This file may be used, modified and/or redistributed under the terms of the 3-clause BSD-License
5 // shipped with this file and also available at: https://github.com/seqan/seqan3/blob/master/LICENSE.md
6 // -----------------------------------------------------------------------------------------------------
7 
13 #pragma once
14 
15 #include <algorithm>
16 #include <concepts>
17 #include <iterator>
18 #include <ranges>
19 #include <span>
20 #include <type_traits>
21 
27 #include <seqan3/io/exception.hpp>
30 
31 namespace seqan3::detail
32 {
33 
34 // ============================================================================
35 // view_take_exactly
36 // ============================================================================
37 
51 template <std::ranges::view urng_t, bool or_throw>
52 class view_take_exactly : public std::ranges::view_interface<view_take_exactly<urng_t, or_throw>>
53 {
54 private:
56  urng_t urange;
57 
59  size_t target_size;
60 
62  template <bool const_range>
63  class basic_iterator;
64 
65 private:
70  using iterator = basic_iterator<false>;
76  using const_iterator = basic_iterator<true>;
78 
79 public:
83  view_take_exactly() = default;
84  view_take_exactly(view_take_exactly const & rhs) = default;
85  view_take_exactly(view_take_exactly && rhs) = default;
86  view_take_exactly & operator=(view_take_exactly const & rhs) = default;
87  view_take_exactly & operator=(view_take_exactly && rhs) = default;
88  ~view_take_exactly() = default;
89 
95  constexpr view_take_exactly(urng_t _urange, size_t const _size) : urange{std::move(_urange)}, target_size{_size}
96  {
97  if constexpr (std::ranges::sized_range<urng_t>)
98  {
99  if (std::ranges::size(urange) < target_size)
100  {
101  if constexpr (or_throw)
102  {
103  throw std::invalid_argument{
104  "You are trying to construct a detail::take_exactly_or_throw from a range that is strictly "
105  "smaller."};
106  }
107  else
108  {
109  target_size = std::ranges::size(urange);
110  }
111  }
112  }
113  }
114 
121  template <std::ranges::viewable_range rng_t>
122  requires std::constructible_from<rng_t, std::views::all_t<rng_t>>
123  constexpr view_take_exactly(rng_t && _urange, size_t const _size) :
124  view_take_exactly{std::views::all(std::forward<rng_t>(_urange)), _size}
125  {}
127 
144  constexpr auto begin() noexcept
145  {
146  if constexpr (std::ranges::random_access_range<urng_t> && std::ranges::sized_range<urng_t>)
147  return std::ranges::begin(urange);
148  else
149  return iterator{std::ranges::begin(urange), 0, target_size, this};
150  }
151 
153  constexpr auto begin() const noexcept
154  requires const_iterable_range<urng_t> && std::ranges::forward_range<urng_t>
155  {
156  if constexpr (std::ranges::random_access_range<urng_t> && std::ranges::sized_range<urng_t>)
157  return std::ranges::cbegin(urange);
158  else
159  {
160  // const_iterator does not work if the underlying iterator is a std::input_iterator (it needs to access
161  // target_size)
162  return const_iterator{std::ranges::cbegin(urange), 0, target_size};
163  }
164  }
165 
179  constexpr auto end() noexcept
180  {
181  if constexpr (std::ranges::random_access_range<urng_t> && std::ranges::sized_range<urng_t>)
182  return std::ranges::begin(urange) + target_size;
183  else
184  return std::ranges::end(urange);
185  }
186 
188  constexpr auto end() const noexcept
189  requires const_iterable_range<urng_t> && std::ranges::forward_range<urng_t>
190  {
191  if constexpr (std::ranges::random_access_range<urng_t> && std::ranges::sized_range<urng_t>)
192  return std::ranges::cbegin(urange) + target_size;
193  else
194  return std::ranges::cend(urange);
195  }
197 
209  constexpr auto size() const noexcept
210  {
211  return target_size;
212  }
213 };
214 
217 template <typename urng_t, bool or_throw = false>
218 view_take_exactly(urng_t &&, size_t) -> view_take_exactly<std::views::all_t<urng_t>, or_throw>;
219 
222 template <std::ranges::view urng_t, bool or_throw>
223 template <bool const_range>
224 class view_take_exactly<urng_t, or_throw>::basic_iterator :
225  public inherited_iterator_base<basic_iterator<const_range>, maybe_const_iterator_t<const_range, urng_t>>
226 {
227 private:
229  using base_base_t = maybe_const_iterator_t<const_range, urng_t>;
231  using base_t = inherited_iterator_base<basic_iterator, maybe_const_iterator_t<const_range, urng_t>>;
232 
234  using sentinel_type = maybe_const_sentinel_t<const_range, urng_t>;
235 
237  size_t pos{};
238 
240  size_t max_pos{};
241 
243  std::conditional_t<!std::forward_iterator<base_base_t>, view_take_exactly *, detail::ignore_t> host_ptr;
244 
245 public:
250  basic_iterator() = default;
251  basic_iterator(basic_iterator const & rhs) = default;
252  basic_iterator(basic_iterator && rhs) = default;
253  basic_iterator & operator=(basic_iterator const & rhs) = default;
254  basic_iterator & operator=(basic_iterator && rhs) = default;
255  ~basic_iterator() = default;
256 
258  constexpr basic_iterator(base_base_t const & it) noexcept(noexcept(base_t{it})) : base_t{std::move(it)}
259  {}
260 
262  constexpr basic_iterator(base_base_t it,
263  size_t const _pos,
264  size_t const _max_pos,
265  view_take_exactly * host = nullptr) noexcept(noexcept(base_t{it})) :
266  base_t{std::move(it)},
267  pos{_pos},
268  max_pos(_max_pos)
269  {
270  host_ptr = host;
271 
272  if constexpr (!std::forward_iterator<base_base_t>)
273  {
274  assert(host_ptr != nullptr);
275  }
276  }
278 
279  using typename base_t::difference_type;
280  using typename base_t::reference;
281 
288  constexpr basic_iterator & operator++() noexcept(noexcept(++std::declval<base_t &>()))
289  {
290  base_t::operator++();
291  ++pos;
292  if constexpr (!std::forward_iterator<base_base_t>)
293  --host_ptr->target_size;
294  return *this;
295  }
296 
298  constexpr decltype(auto) operator++(int) noexcept(noexcept(++std::declval<basic_iterator &>())
299  && (std::same_as<decltype(std::declval<base_base_t &>()++), void>
300  || std::is_nothrow_copy_constructible_v<basic_iterator>))
301  {
302  // if underlying iterator is a C++20 input iterator (i.e. returns void), return void too.
303  if constexpr (std::same_as<decltype(std::declval<base_base_t &>()++), void>)
304  {
305  ++(*this);
306  }
307  else
308  {
309  basic_iterator cpy{*this};
310  ++(*this);
311  return cpy;
312  }
313  }
314 
316  constexpr basic_iterator & operator--() noexcept(noexcept(--std::declval<base_base_t &>()))
317  requires std::bidirectional_iterator<base_base_t>
318  {
319  base_t::operator--();
320  --pos;
321  return *this;
322  }
323 
325  constexpr basic_iterator operator--(int) noexcept(noexcept(--std::declval<basic_iterator &>())
326  && std::is_nothrow_copy_constructible_v<basic_iterator>)
327  requires std::bidirectional_iterator<base_base_t>
328  {
329  basic_iterator cpy{*this};
330  --(*this);
331  return cpy;
332  }
333 
335  constexpr basic_iterator & operator+=(difference_type const skip) noexcept(noexcept(std::declval<base_t &>() +=
336  skip))
337  requires std::random_access_iterator<base_base_t>
338  {
339  base_t::operator+=(skip);
340  pos += skip;
341  return *this;
342  }
343 
345  constexpr basic_iterator & operator-=(difference_type const skip) noexcept(noexcept(std::declval<base_t &>() -=
346  skip))
347  requires std::random_access_iterator<base_base_t>
348  {
349  base_t::operator-=(skip);
350  pos -= skip;
351  return *this;
352  }
354 
361  constexpr bool operator==(basic_iterator const & rhs) const
362  noexcept(!or_throw && noexcept(std::declval<base_base_t &>() == std::declval<base_base_t &>()))
363  requires std::forward_iterator<base_base_t>
364  {
365  return this->base() == rhs.base();
366  }
367 
369  constexpr bool operator==(sentinel_type const & rhs) const
370  noexcept(!or_throw && noexcept(std::declval<base_base_t const &>() == std::declval<sentinel_type const &>()))
371  {
372  if (pos >= max_pos)
373  return true;
374 
375  if (this->base() == rhs)
376  {
377  if constexpr (or_throw)
378  throw unexpected_end_of_input{"Reached end of input before designated size."};
379 
380  return true;
381  }
382  else
383  {
384  return false;
385  }
386  }
387 
389  constexpr friend bool operator==(sentinel_type const & lhs,
390  basic_iterator const & rhs) noexcept(noexcept(rhs == lhs))
391  {
392  return rhs == lhs;
393  }
394 
396  constexpr bool operator!=(sentinel_type const & rhs) const
397  noexcept(noexcept(std::declval<basic_iterator &>() == rhs))
398  {
399  return !(*this == rhs);
400  }
401 
403  constexpr bool operator!=(basic_iterator const & rhs) const
404  noexcept(noexcept(std::declval<basic_iterator &>() == rhs))
405  requires std::forward_iterator<base_base_t>
406  {
407  return !(*this == rhs);
408  }
409 
411  constexpr friend bool operator!=(sentinel_type const & lhs,
412  basic_iterator const & rhs) noexcept(noexcept(rhs != lhs))
413  {
414  return rhs != lhs;
415  }
417 
427  constexpr reference operator[](std::make_unsigned_t<difference_type> const n) const
428  noexcept(noexcept(std::declval<base_base_t &>()[0]))
429  requires std::random_access_iterator<base_base_t>
430  {
431  return base_t::operator[](n);
432  }
434 };
435 
436 // ============================================================================
437 // take_fn (adaptor definition)
438 // ============================================================================
439 
443 template <bool or_throw>
444 struct take_exactly_fn
445 {
447  constexpr auto operator()(size_t const size) const
448  {
449  return adaptor_from_functor{*this, size};
450  }
451 
455  template <std::ranges::range urng_t>
456  constexpr auto operator()(urng_t && urange, size_t target_size) const
457  {
458  static_assert(std::ranges::viewable_range<urng_t>,
459  "The views::take adaptor can only be passed viewable_ranges, i.e. Views or &-to-non-View.");
460 
461  // safeguard against wrong size
462  if constexpr (std::ranges::sized_range<urng_t>)
463  {
464  if constexpr (or_throw)
465  {
466  if (target_size > std::ranges::size(urange))
467  {
468  throw std::invalid_argument{"You are trying to construct a detail::take_exactly_or_throw from a "
469  "range that is strictly smaller."};
470  }
471  }
472  else
473  {
474  target_size = std::min<size_t>(target_size, std::ranges::size(urange));
475  }
476  }
477 
478  // string_view
479  if constexpr (is_type_specialisation_of_v<std::remove_cvref_t<urng_t>, std::basic_string_view>)
480  {
481  // in standard
482  return urange.substr(0, target_size);
483  }
484  // string const &
485  else if constexpr (is_type_specialisation_of_v<std::remove_cvref_t<urng_t>, std::basic_string>
487  {
488  // not in standard
489  // seqan3::views::type_reduce does this too
490  return std::basic_string_view{std::ranges::data(urange), target_size};
491  }
492  // contiguous
493  else if constexpr (std::ranges::borrowed_range<urng_t> && std::ranges::contiguous_range<urng_t>
494  && std::ranges::sized_range<urng_t>)
495  {
496  // not in standard (special case for std::span in standard)
497  // seqan3::views::type_reduce does this too
498  return std::span{std::ranges::data(urange), target_size};
499  }
500  // random_access
501  else if constexpr (std::ranges::borrowed_range<urng_t> && std::ranges::random_access_range<urng_t>
502  && std::ranges::sized_range<urng_t>)
503  {
504  // not in standard
505  // seqan3::views::type_reduce does this too
506  return std::ranges::subrange<std::ranges::iterator_t<urng_t>, std::ranges::iterator_t<urng_t>>{
507  std::ranges::begin(urange),
508  std::ranges::begin(urange) + target_size,
509  target_size};
510  }
511  // our type
512  else
513  {
514  return view_take_exactly<std::views::all_t<urng_t>, or_throw>{std::forward<urng_t>(urange), target_size};
515  }
516  }
517 };
518 
519 } // namespace seqan3::detail
520 
521 // ============================================================================
522 // detail::take_exactly (adaptor instance definition)
523 // ============================================================================
524 
525 namespace seqan3::detail
526 {
576 inline constexpr auto take_exactly = take_exactly_fn<false>{};
577 
578 // ============================================================================
579 // detail::take_exactly_or_throw (adaptor instance definition)
580 // ============================================================================
581 
590 inline constexpr auto take_exactly_or_throw = take_exactly_fn<true>{};
591 } // namespace seqan3::detail
Provides seqan3::detail::adaptor_from_functor.
T begin(T... args)
The <concepts> header from C++20's standard library.
Provides various transformation traits used by the range module.
T end(T... args)
T forward(T... args)
requires requires
The rank_type of the semi-alphabet; defined as the return type of seqan3::to_rank....
Definition: alphabet/concept.hpp:164
constexpr size_t size
The size of a type pack.
Definition: type_pack/traits.hpp:146
Provides the seqan3::detail::inherited_iterator_base template.
Specifies requirements of an input range type for which the const version of that type satisfies the ...
Provides exceptions used in the I/O module.
T is_const_v
Provides various transformation traits for use on iterators.
SeqAn specific customisations in the standard namespace.
T operator!=(T... args)
The <ranges> header from C++20's standard library.
Provides std::span from the C++20 standard library.
Provides type traits for working with templates.
Provides seqan3::detail::transformation_trait_or.
Additional non-standard concepts for ranges.