SeqAn3  3.1.0
The Modern C++ library for sequence analysis.
take_exactly_view.hpp
Go to the documentation of this file.
1 // -----------------------------------------------------------------------------------------------------
2 // Copyright (c) 2006-2021, Knut Reinert & Freie Universität Berlin
3 // Copyright (c) 2016-2021, 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 <seqan3/std/algorithm>
16 #include <seqan3/std/concepts>
17 #include <seqan3/std/iterator>
18 #include <seqan3/std/ranges>
19 #include <seqan3/std/span>
20 #include <seqan3/std/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)
96  : urange{std::move(_urange)}, target_size{_size}
97  {
98  if constexpr (std::ranges::sized_range<urng_t>)
99  {
100  if (std::ranges::size(urange) < target_size)
101  {
102  if constexpr (or_throw)
103  {
105  {
106  "You are trying to construct a detail::take_exactly_or_throw from a range that is strictly "
107  "smaller."
108  };
109  }
110  else
111  {
112  target_size = std::ranges::size(urange);
113  }
114  }
115  }
116  }
117 
124  template <std::ranges::viewable_range rng_t>
126  requires std::constructible_from<rng_t, std::views::all_t<rng_t>>
128  constexpr view_take_exactly(rng_t && _urange, size_t const _size)
129  : view_take_exactly{std::views::all(std::forward<rng_t>(_urange)), _size}
130  {}
132 
149  constexpr auto begin() noexcept
150  {
151  if constexpr (std::ranges::random_access_range<urng_t> && std::ranges::sized_range<urng_t>)
152  return std::ranges::begin(urange);
153  else
154  return iterator{std::ranges::begin(urange), 0, target_size, this};
155  }
156 
158  constexpr auto begin() const noexcept
159  requires const_iterable_range<urng_t>
160  {
161  if constexpr (std::ranges::random_access_range<urng_t> && std::ranges::sized_range<urng_t>)
162  return std::ranges::cbegin(urange);
163  else
164  return const_iterator{std::ranges::cbegin(urange), 0, target_size};
165  }
166 
180  constexpr auto end() noexcept
181  {
182  if constexpr (std::ranges::random_access_range<urng_t> && std::ranges::sized_range<urng_t>)
183  return std::ranges::begin(urange) + target_size;
184  else
185  return std::ranges::end(urange);
186  }
187 
189  constexpr auto end() const noexcept
190  requires const_iterable_range<urng_t>
191  {
192  if constexpr (std::ranges::random_access_range<urng_t> && std::ranges::sized_range<urng_t>)
193  return std::ranges::cbegin(urange) + target_size;
194  else
195  return std::ranges::cend(urange);
196  }
198 
210  constexpr auto size() const noexcept
211  {
212  return target_size;
213  }
214 };
215 
218 template <typename urng_t,
219  bool or_throw = false>
220 view_take_exactly(urng_t && , size_t) -> view_take_exactly<std::views::all_t<urng_t>, or_throw>;
221 
224 template <std::ranges::view urng_t, bool or_throw>
225 template <bool const_range>
226 class view_take_exactly<urng_t, or_throw>::basic_iterator :
227  public inherited_iterator_base<basic_iterator<const_range>, maybe_const_iterator_t<const_range, urng_t>>
228 {
229 private:
231  using base_base_t = maybe_const_iterator_t<const_range, urng_t>;
233  using base_t = inherited_iterator_base<basic_iterator, maybe_const_iterator_t<const_range, urng_t>>;
234 
236  using sentinel_type = maybe_const_sentinel_t<const_range, urng_t>;
237 
239  size_t pos{};
240 
242  size_t max_pos{};
243 
245  std::conditional_t<!std::forward_iterator<base_base_t>, view_take_exactly *, detail::ignore_t> host_ptr;
246 
247 public:
252  basic_iterator() = default;
253  basic_iterator(basic_iterator const & rhs) = default;
254  basic_iterator(basic_iterator && rhs) = default;
255  basic_iterator & operator=(basic_iterator const & rhs) = default;
256  basic_iterator & operator=(basic_iterator && rhs) = default;
257  ~basic_iterator() = default;
258 
260  constexpr basic_iterator(base_base_t const & it) noexcept(noexcept(base_t{it})) :
261  base_t{std::move(it)}
262  {}
263 
265  constexpr basic_iterator(base_base_t it,
266  size_t const _pos,
267  size_t const _max_pos,
268  view_take_exactly * host = nullptr) noexcept(noexcept(base_t{it})) :
269  base_t{std::move(it)}, pos{_pos}, max_pos(_max_pos)
270  {
271  host_ptr = host;
272  }
274 
275  using typename base_t::difference_type;
276  using typename base_t::reference;
277 
284  constexpr basic_iterator & operator++() noexcept(noexcept(++std::declval<base_t &>()))
285  {
286  base_t::operator++();
287  ++pos;
288  if constexpr (!std::forward_iterator<base_base_t>)
289  --host_ptr->target_size;
290  return *this;
291  }
292 
294  constexpr basic_iterator operator++(int) noexcept(noexcept(++std::declval<basic_iterator &>()) &&
295  std::is_nothrow_copy_constructible_v<basic_iterator>)
296  {
297  basic_iterator cpy{*this};
298  ++(*this);
299  return cpy;
300  }
301 
303  constexpr basic_iterator & operator--() noexcept(noexcept(--std::declval<base_base_t &>()))
305  requires std::bidirectional_iterator<base_base_t>
307  {
308  base_t::operator--();
309  --pos;
310  return *this;
311  }
312 
314  constexpr basic_iterator operator--(int) noexcept(noexcept(--std::declval<basic_iterator &>()) &&
315  std::is_nothrow_copy_constructible_v<basic_iterator>)
317  requires std::bidirectional_iterator<base_base_t>
319  {
320  basic_iterator cpy{*this};
321  --(*this);
322  return cpy;
323  }
324 
326  constexpr basic_iterator & operator+=(difference_type const skip)
327  noexcept(noexcept(std::declval<base_t &>() += skip))
329  requires std::random_access_iterator<base_base_t>
331  {
332  base_t::operator+=(skip);
333  pos += skip;
334  return *this;
335  }
336 
338  constexpr basic_iterator & operator-=(difference_type const skip)
339  noexcept(noexcept(std::declval<base_t &>() -= skip))
341  requires std::random_access_iterator<base_base_t>
343  {
344  base_t::operator-=(skip);
345  pos -= skip;
346  return *this;
347  }
349 
356  constexpr bool operator==(basic_iterator const & rhs) const
357  noexcept(!or_throw && noexcept(std::declval<base_base_t &>() == std::declval<base_base_t &>()))
359  requires std::forward_iterator<base_base_t>
361  {
362  return this->base() == rhs.base();
363  }
364 
366  constexpr bool operator==(sentinel_type const & rhs) const
367  noexcept(!or_throw && noexcept(std::declval<base_base_t const &>() == std::declval<sentinel_type const &>()))
368  {
369  if (pos >= max_pos)
370  return true;
371 
372  if (this->base() == rhs)
373  {
374  if constexpr (or_throw)
375  throw unexpected_end_of_input{"Reached end of input before designated size."};
376 
377  return true;
378  }
379  else
380  {
381  return false;
382  }
383  }
384 
386  constexpr friend bool operator==(sentinel_type const & lhs, basic_iterator const & rhs)
387  noexcept(noexcept(rhs == lhs))
388  {
389  return rhs == lhs;
390  }
391 
393  constexpr bool operator!=(sentinel_type const & rhs) const
394  noexcept(noexcept(std::declval<basic_iterator &>() == rhs))
395  {
396  return !(*this == rhs);
397  }
398 
400  constexpr bool operator!=(basic_iterator const & rhs) const
401  noexcept(noexcept(std::declval<basic_iterator &>() == rhs))
403  requires std::forward_iterator<base_base_t>
405  {
406  return !(*this == rhs);
407  }
408 
410  constexpr friend bool operator!=(sentinel_type const & lhs, basic_iterator const & rhs)
411  noexcept(noexcept(rhs != lhs))
412  {
413  return rhs != lhs;
414  }
416 
426  constexpr reference operator[](std::make_unsigned_t<difference_type> const n) const
427  noexcept(noexcept(std::declval<base_base_t &>()[0]))
429  requires std::random_access_iterator<base_base_t>
431  {
432  return base_t::operator[](n);
433  }
435 };
436 
437 // ============================================================================
438 // take_fn (adaptor definition)
439 // ============================================================================
440 
444 template <bool or_throw>
445 struct take_exactly_fn
446 {
448  constexpr auto operator()(size_t const size) const
449  {
450  return adaptor_from_functor{*this, size};
451  }
452 
456  template <std::ranges::range urng_t>
457  constexpr auto operator()(urng_t && urange, size_t target_size) const
458  {
459  static_assert(std::ranges::viewable_range<urng_t>,
460  "The views::take adaptor can only be passed viewable_ranges, i.e. Views or &-to-non-View.");
461 
462  // safeguard against wrong size
463  if constexpr (std::ranges::sized_range<urng_t>)
464  {
465  if constexpr (or_throw)
466  {
467  if (target_size > std::ranges::size(urange))
468  {
469  throw std::invalid_argument{"You are trying to construct a detail::take_exactly_or_throw from a "
470  "range that is strictly smaller."};
471  }
472  }
473  else
474  {
475  target_size = std::min<size_t>(target_size, std::ranges::size(urange));
476  }
477  }
478 
479  // string_view
480  if constexpr (is_type_specialisation_of_v<std::remove_cvref_t<urng_t>, std::basic_string_view>)
481  {
482  // in standard
483  return urange.substr(0, target_size);
484  }
485  // string const &
486  else if constexpr (is_type_specialisation_of_v<std::remove_cvref_t<urng_t>, std::basic_string> &&
488  {
489  // not in standard
490  // seqan3::views::type_reduce does this too
491  return std::basic_string_view{std::ranges::data(urange), target_size};
492  }
493  // contiguous
494  else if constexpr (std::ranges::borrowed_range<urng_t> &&
495  std::ranges::contiguous_range<urng_t> &&
496  std::ranges::sized_range<urng_t>)
497  {
498  // not in standard (special case for std::span in standard)
499  // seqan3::views::type_reduce does this too
500  return std::span{std::ranges::data(urange), target_size};
501  }
502  // random_access
503  else if constexpr (std::ranges::borrowed_range<urng_t> &&
504  std::ranges::random_access_range<urng_t> &&
505  std::ranges::sized_range<urng_t>)
506  {
507  // not in standard
508  // seqan3::views::type_reduce does this too
509  return std::ranges::subrange<std::ranges::iterator_t<urng_t>, std::ranges::iterator_t<urng_t>>
510  {
511  std::ranges::begin(urange),
512  std::ranges::begin(urange) + target_size,
513  target_size
514  };
515  }
516  // our type
517  else
518  {
519  return view_take_exactly<std::views::all_t<urng_t>, or_throw>
520  {
521  std::forward<urng_t>(urange),
522  target_size
523  };
524  }
525  }
526 };
527 
528 } // namespace seqan3::detail
529 
530 // ============================================================================
531 // detail::take_exactly (adaptor instance definition)
532 // ============================================================================
533 
534 namespace seqan3::detail
535 {
585 inline auto constexpr take_exactly = take_exactly_fn<false>{};
586 
587 // ============================================================================
588 // detail::take_exactly_or_throw (adaptor instance definition)
589 // ============================================================================
590 
599 inline auto constexpr take_exactly_or_throw = take_exactly_fn<true>{};
600 } // namespace seqan3::detail
Provides seqan3::detail::adaptor_from_functor.
The <algorithm> header from C++20's standard library.
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)
typename remove_cvref< t >::type remove_cvref_t
Return the input type with const, volatile and references removed (transformation_trait shortcut).
Definition: type_traits:73
constexpr size_t size
The size of a type pack.
Definition: traits.hpp:151
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.
The <iterator> header from C++20's standard library.
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.
The <type_traits> header from C++20's standard library.
Additional non-standard concepts for ranges.