SeqAn3  3.2.0-rc.1
The Modern C++ library for sequence analysis.
take_until_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 <type_traits>
20 
26 #include <seqan3/io/exception.hpp>
29 
30 namespace seqan3::detail
31 {
32 
33 // ============================================================================
34 // view_take_until
35 // ============================================================================
36 
50 template <std::ranges::view urng_t, typename fun_t, bool or_throw, bool and_consume>
51 class view_take_until : public std::ranges::view_interface<view_take_until<urng_t, fun_t, or_throw, and_consume>>
52 {
53 private:
54  static_assert(std::invocable<fun_t, std::ranges::range_reference_t<urng_t>>,
55  "The functor type for detail::take_until must model"
56  "std::invocable<fun_t, std::ranges::range_reference_t<urng_t>>.");
57  static_assert(std::convertible_to<std::result_of_t<fun_t && (std::ranges::range_reference_t<urng_t>)>, bool>,
58  "The result type of the functor for detail::take_until must be a boolean.");
59 
61  urng_t urange;
62 
64  copyable_wrapper_t<fun_t> fun;
65 
67  static constexpr bool const_iterable =
68  const_iterable_range<urng_t> && indirect_unary_predicate_on_range<fun_t const, urng_t const>;
69 
72  template <bool const_range>
73  using basic_iterator = seqan3::detail::maybe_const_iterator_t<const_range, urng_t>;
74 
77  template <bool const_range>
78  class basic_sentinel;
79 
82  template <bool const_range>
83  class basic_consume_iterator;
84 
87  template <bool const_range>
88  using basic_consume_sentinel = std::default_sentinel_t;
89 
90 public:
94  view_take_until() = default;
95  constexpr view_take_until(view_take_until const & rhs) = default;
96  constexpr view_take_until(view_take_until && rhs) = default;
97  constexpr view_take_until & operator=(view_take_until const & rhs) = default;
98  constexpr view_take_until & operator=(view_take_until && rhs) = default;
99  ~view_take_until() = default;
100 
105  view_take_until(urng_t && _urange, fun_t && _fun) :
106  urange{std::forward<urng_t>(_urange)},
107  fun{std::forward<fun_t>(_fun)}
108  {}
109 
115  template <std::ranges::viewable_range rng_t>
116  requires std::constructible_from<urng_t, std::views::all_t<rng_t>>
117  view_take_until(rng_t && _urange, fun_t && _fun) :
118  view_take_until{std::views::all(std::forward<rng_t>(_urange)), std::forward<fun_t>(_fun)}
119  {}
121 
138  auto begin() noexcept
139  {
140  if constexpr (and_consume && !std::ranges::forward_range<urng_t>)
141  return basic_consume_iterator<false>{std::ranges::begin(urange), fun, std::ranges::end(urange)};
142  else
143  return basic_iterator<false>{std::ranges::begin(urange)};
144  }
145 
147  auto begin() const noexcept
148  requires const_iterable
149  {
150  if constexpr (and_consume && !std::ranges::forward_range<urng_t const>)
151  return basic_consume_iterator<true>{std::ranges::cbegin(urange), fun, std::ranges::cend(urange)};
152  else
153  return basic_iterator<true>{std::ranges::cbegin(urange)};
154  }
155 
169  auto end() noexcept
170  {
171  if constexpr (and_consume && !std::ranges::forward_range<urng_t>)
172  return basic_consume_sentinel<false>{};
173  else
174  return basic_sentinel<false>{std::ranges::end(urange), fun};
175  }
176 
178  auto end() const noexcept
179  requires const_iterable
180  {
181  if constexpr (and_consume && !std::ranges::forward_range<urng_t const>)
182  return basic_consume_sentinel<true>{};
183  else
184  return basic_sentinel<true>{std::ranges::cend(urange), fun};
185  }
187 };
188 
191 template <typename urng_t, typename fun_t, bool or_throw = false, bool and_consume = false>
192 view_take_until(urng_t &&, fun_t &&) -> view_take_until<std::views::all_t<urng_t>, fun_t, or_throw, and_consume>;
193 
194 template <std::ranges::view urng_t, typename fun_t, bool or_throw, bool and_consume>
195 template <bool const_range>
196 class view_take_until<urng_t, fun_t, or_throw, and_consume>::basic_consume_iterator :
197  public inherited_iterator_base<basic_consume_iterator<const_range>,
198  seqan3::detail::maybe_const_iterator_t<const_range, urng_t>>
199 {
200 private:
202  using underlying_iterator_t = seqan3::detail::maybe_const_iterator_t<const_range, urng_t>;
204  using base_t = inherited_iterator_base<basic_consume_iterator, underlying_iterator_t>;
205 
207  copyable_wrapper_t<fun_t> const * fun{nullptr};
208 
210  using underlying_sentinel_t = seqan3::detail::maybe_const_sentinel_t<const_range, urng_t>;
211 
213  underlying_sentinel_t underlying_sentinel;
214 
216  bool at_end_gracefully = false;
217 
218 public:
223  constexpr basic_consume_iterator() = default;
224  constexpr basic_consume_iterator(basic_consume_iterator const & rhs) = default;
225  constexpr basic_consume_iterator(basic_consume_iterator && rhs) = default;
226  constexpr basic_consume_iterator & operator=(basic_consume_iterator const & rhs) = default;
227  constexpr basic_consume_iterator & operator=(basic_consume_iterator && rhs) = default;
228  ~basic_consume_iterator() = default;
229 
231  basic_consume_iterator(underlying_iterator_t it,
232  copyable_wrapper_t<fun_t> const & _fun,
233  underlying_sentinel_t sen) noexcept(noexcept(base_t{it})) :
234  base_t{std::move(it)},
235  fun{std::addressof(_fun)},
236  underlying_sentinel{std::move(sen)}
237  {
238  if ((this->base() != underlying_sentinel) && fun->operator()(**this))
239  {
240  at_end_gracefully = true;
241  ++(*this);
242  }
243  }
245 
250  using difference_type = std::iter_difference_t<underlying_iterator_t>;
251  using value_type = std::iter_value_t<underlying_iterator_t>;
252  using reference = std::iter_reference_t<underlying_iterator_t>;
253  using pointer = detail::iter_pointer_t<underlying_iterator_t>;
254  using iterator_category = std::input_iterator_tag;
256 
262  basic_consume_iterator & operator++() noexcept(noexcept(++std::declval<base_t &>()) && noexcept(
263  std::declval<underlying_iterator_t &>()
264  != std::declval<underlying_sentinel_t &>()) && noexcept(fun->operator()(std::declval<reference>())))
265  {
266  base_t::operator++();
267 
268  while ((this->base() != underlying_sentinel) && fun->operator()(**this))
269  {
270  at_end_gracefully = true;
271  base_t::operator++();
272  }
273 
274  return *this;
275  }
276 
278  decltype(auto) operator++(int) noexcept(noexcept(++std::declval<basic_consume_iterator &>())
279  && (std::same_as<decltype(std::declval<underlying_iterator_t &>()++), void>
280  || std::is_nothrow_copy_constructible_v<basic_consume_iterator>))
281  {
282  // if underlying iterator is a C++20 input iterator (i.e. returns void), return void too.
283  if constexpr (std::same_as<decltype(std::declval<underlying_iterator_t &>()++), void>)
284  {
285  ++(*this);
286  }
287  else
288  {
289  basic_consume_iterator cpy{*this};
290  ++(*this);
291  return cpy;
292  }
293  }
295 
300  bool operator==(basic_consume_sentinel<const_range> const &) const noexcept(!or_throw && noexcept(
301  std::declval<underlying_iterator_t &>()
302  != std::declval<underlying_sentinel_t &>()) && noexcept(fun->operator()(std::declval<reference>())))
303  {
304  if (at_end_gracefully)
305  return true;
306 
307  if (this->base() == underlying_sentinel)
308  {
309  if constexpr (or_throw)
310  throw unexpected_end_of_input{"Reached end of input before functor evaluated to true."};
311  else
312  return true;
313  }
314 
315  return fun->operator()(**this);
316  }
317 
319  friend bool operator==(basic_consume_sentinel<const_range> const & lhs,
320  basic_consume_iterator const & rhs) noexcept(noexcept(rhs == lhs))
321  {
322  return rhs == lhs;
323  }
324 
326  bool operator!=(basic_consume_sentinel<const_range> const & rhs) const
327  noexcept(noexcept(std::declval<basic_consume_iterator &>() == rhs))
328  {
329  return !(*this == rhs);
330  }
331 
333  friend bool operator!=(basic_consume_sentinel<const_range> const & lhs,
334  basic_consume_iterator const & rhs) noexcept(noexcept(rhs != lhs))
335  {
336  return rhs != lhs;
337  }
339 };
340 
341 template <std::ranges::view urng_t, typename fun_t, bool or_throw, bool and_consume>
342 template <bool const_range>
343 class view_take_until<urng_t, fun_t, or_throw, and_consume>::basic_sentinel
344 {
345 private:
347  using underlying_sentinel_t = seqan3::detail::maybe_const_sentinel_t<const_range, urng_t>;
348 
350  underlying_sentinel_t underlying_sentinel{};
351 
353  copyable_wrapper_t<fun_t> const * fun{nullptr};
354 
355 public:
359  basic_sentinel() = default;
360  basic_sentinel(basic_sentinel const &) = default;
361  basic_sentinel(basic_sentinel &&) = default;
362  basic_sentinel & operator=(basic_sentinel const &) = default;
363  basic_sentinel & operator=(basic_sentinel &&) = default;
364  ~basic_sentinel() = default;
365 
370  explicit basic_sentinel(underlying_sentinel_t underlying_sentinel, copyable_wrapper_t<fun_t> const & _fun) :
371  underlying_sentinel{std::move(underlying_sentinel)},
372  fun{std::addressof(_fun)}
373  {}
374 
376  basic_sentinel(basic_sentinel<!const_range> other)
377  requires const_range && std::convertible_to<std::ranges::sentinel_t<urng_t>, underlying_sentinel_t>
378  : underlying_sentinel{std::move(other.underlying_sentinel)}, fun{other.fun}
379  {}
381 
387  friend bool operator==(basic_iterator<const_range> const & lhs, basic_sentinel const & rhs)
388  {
389  // Actual comparison delegated to lhs base
390  if (lhs == rhs.underlying_sentinel)
391  {
392  if constexpr (or_throw)
393  throw unexpected_end_of_input{"Reached end of input before functor evaluated to true."};
394  else
395  return true;
396  }
397 
398  return rhs.fun->operator()(*lhs);
399  }
400 
402  friend bool operator==(basic_sentinel const & lhs, basic_iterator<const_range> const & rhs)
403  {
404  return rhs == lhs;
405  }
406 
408  friend bool operator!=(basic_iterator<const_range> const & lhs, basic_sentinel const & rhs)
409  {
410  return !(lhs == rhs);
411  }
412 
414  friend bool operator!=(basic_sentinel const & lhs, basic_iterator<const_range> const & rhs)
415  {
416  return rhs != lhs;
417  }
418 
420  template <bool other_const_range = !const_range>
421  requires (std::sentinel_for<underlying_sentinel_t, basic_iterator<other_const_range>>)
422  friend bool operator==(basic_iterator<other_const_range> const & lhs, basic_sentinel const & rhs)
423  {
424  // Actual comparison delegated to lhs base
425  if (lhs == rhs.underlying_sentinel)
426  {
427  if constexpr (or_throw)
428  throw unexpected_end_of_input{"Reached end of input before functor evaluated to true."};
429  else
430  return true;
431  }
432 
433  return rhs.fun->operator()(*lhs);
434  }
435 
437  template <bool other_const_range = !const_range>
438  requires (std::sentinel_for<underlying_sentinel_t, basic_iterator<other_const_range>>)
439  friend bool operator==(basic_sentinel const & lhs, basic_iterator<other_const_range> const & rhs)
440  {
441  return rhs == lhs;
442  }
443 
445  template <bool other_const_range = !const_range>
446  requires (std::sentinel_for<underlying_sentinel_t, basic_iterator<other_const_range>>)
447  friend bool operator!=(basic_iterator<other_const_range> const & lhs, basic_sentinel const & rhs)
448  {
449  return !(lhs == rhs);
450  }
451 
453  template <bool other_const_range = !const_range>
454  requires (std::sentinel_for<underlying_sentinel_t, basic_iterator<other_const_range>>)
455  friend bool operator!=(basic_sentinel const & lhs, basic_iterator<other_const_range> const & rhs)
456  {
457  return rhs != lhs;
458  }
460 };
461 
462 // ============================================================================
463 // take_until_fn (adaptor definition)
464 // ============================================================================
465 
469 template <bool or_throw, bool and_consume>
470 struct take_until_fn
471 {
473  template <typename fun_t>
474  constexpr auto operator()(fun_t && fun) const
475  {
476  return adaptor_from_functor{*this, std::forward<fun_t>(fun)};
477  }
478 
486  template <std::ranges::viewable_range urng_t, typename fun_t>
487  constexpr auto operator()(urng_t && urange, fun_t && fun) const
488  {
489  return view_take_until<std::views::all_t<urng_t>, fun_t, or_throw, and_consume>{
490  std::views::all(std::forward<urng_t>(urange)),
491  std::forward<fun_t>(fun)};
492  }
493 };
494 
495 } // namespace seqan3::detail
496 
497 // ============================================================================
498 // detail::take_until (adaptor instance definition)
499 // ============================================================================
500 
501 namespace seqan3::detail
502 {
560 inline constexpr auto take_until = take_until_fn<false, false>{};
561 
562 // ============================================================================
563 // detail::take_until_or_throw (adaptor instance definition)
564 // ============================================================================
565 
574 inline constexpr auto take_until_or_throw = take_until_fn<true, false>{};
575 
576 // ============================================================================
577 // detail::take_until_and_consume (adaptor instance definition)
578 // ============================================================================
579 
588 inline constexpr auto take_until_and_consume = take_until_fn<false, true>{};
589 
590 // ============================================================================
591 // detail::take_until_or_throw_and_consume (adaptor instance definition)
592 // ============================================================================
593 
602 inline constexpr auto take_until_or_throw_and_consume = take_until_fn<true, true>{};
603 
604 } // namespace seqan3::detail
Provides seqan3::detail::adaptor_from_functor.
T addressof(T... args)
T begin(T... args)
The <concepts> header from C++20's standard library.
Provides seqan3::detail::copyable_wrapper.
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
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.
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 seqan3::detail::transformation_trait_or.
Additional non-standard concepts for ranges.