SeqAn3 3.4.0-rc.1
The Modern C++ library for sequence analysis.
Loading...
Searching...
No Matches
take_until_view.hpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2006-2024 Knut Reinert & Freie Universität Berlin
2// SPDX-FileCopyrightText: 2016-2024 Knut Reinert & MPI für molekulare Genetik
3// SPDX-License-Identifier: BSD-3-Clause
4
10#pragma once
11
12#include <algorithm>
13#include <concepts>
14#include <iterator>
15#include <ranges>
16#include <type_traits>
17
26
27namespace seqan3::detail
28{
29
30// ============================================================================
31// view_take_until
32// ============================================================================
33
47template <std::ranges::view urng_t, typename fun_t, bool or_throw, bool and_consume>
48class view_take_until : public std::ranges::view_interface<view_take_until<urng_t, fun_t, or_throw, and_consume>>
49{
50private:
51 static_assert(std::invocable<fun_t, std::ranges::range_reference_t<urng_t>>,
52 "The functor type for detail::take_until must model"
53 "std::invocable<fun_t, std::ranges::range_reference_t<urng_t>>.");
54 static_assert(std::convertible_to<std::invoke_result_t<fun_t &&, std::ranges::range_reference_t<urng_t>>, bool>,
55 "The result type of the functor for detail::take_until must be a boolean.");
56
58 urng_t urange;
59
61 copyable_wrapper_t<fun_t> fun;
62
64 static constexpr bool const_iterable =
65 const_iterable_range<urng_t> && indirect_unary_predicate_on_range<fun_t const, urng_t const>;
66
69 template <bool const_range>
70 using basic_iterator = seqan3::detail::maybe_const_iterator_t<const_range, urng_t>;
71
74 template <bool const_range>
75 class basic_sentinel;
76
79 template <bool const_range>
80 class basic_consume_iterator;
81
84 template <bool const_range>
85 using basic_consume_sentinel = std::default_sentinel_t;
86
87public:
91 view_take_until() = default;
92 constexpr view_take_until(view_take_until const & rhs) = default;
93 constexpr view_take_until(view_take_until && rhs) = default;
94 constexpr view_take_until & operator=(view_take_until const & rhs) = default;
95 constexpr view_take_until & operator=(view_take_until && rhs) = default;
96 ~view_take_until() = default;
97
102 view_take_until(urng_t && _urange, fun_t && _fun) :
103 urange{std::forward<urng_t>(_urange)},
104 fun{std::forward<fun_t>(_fun)}
105 {}
106
112 template <std::ranges::viewable_range rng_t>
113 requires std::constructible_from<urng_t, std::views::all_t<rng_t>>
114 view_take_until(rng_t && _urange, fun_t && _fun) :
115 view_take_until{std::views::all(std::forward<rng_t>(_urange)), std::forward<fun_t>(_fun)}
116 {}
118
135 auto begin() noexcept
136 {
137 if constexpr (and_consume && !std::ranges::forward_range<urng_t>)
138 return basic_consume_iterator<false>{std::ranges::begin(urange), fun, std::ranges::end(urange)};
139 else
140 return basic_iterator<false>{std::ranges::begin(urange)};
141 }
142
144 auto begin() const noexcept
145 requires const_iterable
146 {
147 if constexpr (and_consume && !std::ranges::forward_range<urng_t const>)
148 return basic_consume_iterator<true>{std::ranges::cbegin(urange), fun, std::ranges::cend(urange)};
149 else
150 return basic_iterator<true>{std::ranges::begin(urange)};
151 }
152
166 auto end() noexcept
167 {
168 if constexpr (and_consume && !std::ranges::forward_range<urng_t>)
169 return basic_consume_sentinel<false>{};
170 else
171 return basic_sentinel<false>{std::ranges::end(urange), fun};
172 }
173
175 auto end() const noexcept
176 requires const_iterable
177 {
178 if constexpr (and_consume && !std::ranges::forward_range<urng_t const>)
179 return basic_consume_sentinel<true>{};
180 else
181 return basic_sentinel<true>{std::ranges::end(urange), fun};
182 }
184};
185
188template <typename urng_t, typename fun_t, bool or_throw = false, bool and_consume = false>
189view_take_until(urng_t &&, fun_t &&) -> view_take_until<std::views::all_t<urng_t>, fun_t, or_throw, and_consume>;
190
191template <std::ranges::view urng_t, typename fun_t, bool or_throw, bool and_consume>
192template <bool const_range>
193class view_take_until<urng_t, fun_t, or_throw, and_consume>::basic_consume_iterator :
194 public inherited_iterator_base<basic_consume_iterator<const_range>,
195 seqan3::detail::maybe_const_iterator_t<const_range, urng_t>>
196{
197private:
199 using underlying_iterator_t = seqan3::detail::maybe_const_iterator_t<const_range, urng_t>;
201 using base_t = inherited_iterator_base<basic_consume_iterator, underlying_iterator_t>;
202
204 copyable_wrapper_t<fun_t> const * fun{nullptr};
205
207 using underlying_sentinel_t = seqan3::detail::maybe_const_sentinel_t<const_range, urng_t>;
208
210 underlying_sentinel_t underlying_sentinel;
211
213 bool at_end_gracefully = false;
214
215public:
220 constexpr basic_consume_iterator() = default;
221 constexpr basic_consume_iterator(basic_consume_iterator const & rhs) = default;
222 constexpr basic_consume_iterator(basic_consume_iterator && rhs) = default;
223 constexpr basic_consume_iterator & operator=(basic_consume_iterator const & rhs) = default;
224 constexpr basic_consume_iterator & operator=(basic_consume_iterator && rhs) = default;
225 ~basic_consume_iterator() = default;
226
228 basic_consume_iterator(underlying_iterator_t it, copyable_wrapper_t<fun_t> const & _fun, underlying_sentinel_t sen)
229 noexcept(noexcept(base_t{it})) :
230 base_t{std::move(it)},
231 fun{std::addressof(_fun)},
232 underlying_sentinel{std::move(sen)}
233 {
234 if ((this->base() != underlying_sentinel) && fun->operator()(**this))
235 {
236 at_end_gracefully = true;
237 ++(*this);
238 }
239 }
241
246 using difference_type = std::iter_difference_t<underlying_iterator_t>;
249 using pointer = detail::iter_pointer_t<underlying_iterator_t>;
250 using iterator_category = std::input_iterator_tag;
252
258 basic_consume_iterator & operator++()
259 noexcept(noexcept(++std::declval<base_t &>())
260 && noexcept(std::declval<underlying_iterator_t &>() != std::declval<underlying_sentinel_t &>())
261 && noexcept(fun->operator()(std::declval<reference>())))
262 {
263 base_t::operator++();
264
265 while ((this->base() != underlying_sentinel) && fun->operator()(**this))
266 {
267 at_end_gracefully = true;
268 base_t::operator++();
269 }
270
271 return *this;
272 }
273
275 decltype(auto) operator++(int) noexcept(noexcept(++std::declval<basic_consume_iterator &>())
276 && (std::same_as<decltype(std::declval<underlying_iterator_t &>()++), void>
277 || std::is_nothrow_copy_constructible_v<basic_consume_iterator>))
278 {
279 // if underlying iterator is a C++20 input iterator (i.e. returns void), return void too.
280 if constexpr (std::same_as<decltype(std::declval<underlying_iterator_t &>()++), void>)
281 {
282 ++(*this);
283 }
284 else
285 {
286 basic_consume_iterator cpy{*this};
287 ++(*this);
288 return cpy;
289 }
290 }
292
297 bool operator==(basic_consume_sentinel<const_range> const &) const
298 noexcept(!or_throw
299 && noexcept(std::declval<underlying_iterator_t &>() != std::declval<underlying_sentinel_t &>())
300 && noexcept(fun->operator()(std::declval<reference>())))
301 {
302 if (at_end_gracefully)
303 return true;
304
305 if (this->base() == underlying_sentinel)
306 {
307 if constexpr (or_throw)
308 throw unexpected_end_of_input{"Reached end of input before functor evaluated to true."};
309 else
310 return true;
311 }
312
313 return fun->operator()(**this);
314 }
315
317 friend bool operator==(basic_consume_sentinel<const_range> const & lhs, basic_consume_iterator const & rhs)
318 noexcept(noexcept(rhs == lhs))
319 {
320 return rhs == lhs;
321 }
322
324 bool operator!=(basic_consume_sentinel<const_range> const & rhs) const
325 noexcept(noexcept(std::declval<basic_consume_iterator &>() == rhs))
326 {
327 return !(*this == rhs);
328 }
329
331 friend bool operator!=(basic_consume_sentinel<const_range> const & lhs, basic_consume_iterator const & rhs)
332 noexcept(noexcept(rhs != lhs))
333 {
334 return rhs != lhs;
335 }
337};
338
339template <std::ranges::view urng_t, typename fun_t, bool or_throw, bool and_consume>
340template <bool const_range>
341class view_take_until<urng_t, fun_t, or_throw, and_consume>::basic_sentinel
342{
343private:
345 using underlying_sentinel_t = seqan3::detail::maybe_const_sentinel_t<const_range, urng_t>;
346
348 underlying_sentinel_t underlying_sentinel{};
349
351 copyable_wrapper_t<fun_t> const * fun{nullptr};
352
353public:
357 basic_sentinel() = default;
358 basic_sentinel(basic_sentinel const &) = default;
359 basic_sentinel(basic_sentinel &&) = default;
360 basic_sentinel & operator=(basic_sentinel const &) = default;
361 basic_sentinel & operator=(basic_sentinel &&) = default;
362 ~basic_sentinel() = default;
363
368 explicit basic_sentinel(underlying_sentinel_t underlying_sentinel, copyable_wrapper_t<fun_t> const & _fun) :
369 underlying_sentinel{std::move(underlying_sentinel)},
370 fun{std::addressof(_fun)}
371 {}
372
374 basic_sentinel(basic_sentinel<!const_range> other)
375 requires const_range && std::convertible_to<std::ranges::sentinel_t<urng_t>, underlying_sentinel_t>
376 : underlying_sentinel{std::move(other.underlying_sentinel)}, fun{other.fun}
377 {}
379
385 friend bool operator==(basic_iterator<const_range> const & lhs, basic_sentinel const & rhs)
386 {
387 // Actual comparison delegated to lhs base
388 if (lhs == rhs.underlying_sentinel)
389 {
390 if constexpr (or_throw)
391 throw unexpected_end_of_input{"Reached end of input before functor evaluated to true."};
392 else
393 return true;
394 }
395
396 return rhs.fun->operator()(*lhs);
397 }
398
400 friend bool operator==(basic_sentinel const & lhs, basic_iterator<const_range> const & rhs)
401 {
402 return rhs == lhs;
403 }
404
406 friend bool operator!=(basic_iterator<const_range> const & lhs, basic_sentinel const & rhs)
407 {
408 return !(lhs == rhs);
409 }
410
412 friend bool operator!=(basic_sentinel const & lhs, basic_iterator<const_range> const & rhs)
413 {
414 return rhs != lhs;
415 }
416
418 template <bool other_const_range = !const_range>
419 requires (std::sentinel_for<underlying_sentinel_t, basic_iterator<other_const_range>>)
420 friend bool operator==(basic_iterator<other_const_range> const & lhs, basic_sentinel const & rhs)
421 {
422 // Actual comparison delegated to lhs base
423 if (lhs == rhs.underlying_sentinel)
424 {
425 if constexpr (or_throw)
426 throw unexpected_end_of_input{"Reached end of input before functor evaluated to true."};
427 else
428 return true;
429 }
430
431 return rhs.fun->operator()(*lhs);
432 }
433
435 template <bool other_const_range = !const_range>
436 requires (std::sentinel_for<underlying_sentinel_t, basic_iterator<other_const_range>>)
437 friend bool operator==(basic_sentinel const & lhs, basic_iterator<other_const_range> const & rhs)
438 {
439 return rhs == lhs;
440 }
441
443 template <bool other_const_range = !const_range>
444 requires (std::sentinel_for<underlying_sentinel_t, basic_iterator<other_const_range>>)
445 friend bool operator!=(basic_iterator<other_const_range> const & lhs, basic_sentinel const & rhs)
446 {
447 return !(lhs == rhs);
448 }
449
451 template <bool other_const_range = !const_range>
452 requires (std::sentinel_for<underlying_sentinel_t, basic_iterator<other_const_range>>)
453 friend bool operator!=(basic_sentinel const & lhs, basic_iterator<other_const_range> const & rhs)
454 {
455 return rhs != lhs;
456 }
458};
459
460// ============================================================================
461// take_until_fn (adaptor definition)
462// ============================================================================
463
467template <bool or_throw, bool and_consume>
468struct take_until_fn
469{
471 template <typename fun_t>
472 constexpr auto operator()(fun_t && fun) const
473 {
474 return adaptor_from_functor{*this, std::forward<fun_t>(fun)};
475 }
476
484 template <std::ranges::viewable_range urng_t, typename fun_t>
485 constexpr auto operator()(urng_t && urange, fun_t && fun) const
486 {
487 return view_take_until<std::views::all_t<urng_t>, fun_t, or_throw, and_consume>{
488 std::views::all(std::forward<urng_t>(urange)),
489 std::forward<fun_t>(fun)};
490 }
491};
492
493} // namespace seqan3::detail
494
495// ============================================================================
496// detail::take_until (adaptor instance definition)
497// ============================================================================
498
499namespace seqan3::detail
500{
558inline constexpr auto take_until = take_until_fn<false, false>{};
559
560// ============================================================================
561// detail::take_until_or_throw (adaptor instance definition)
562// ============================================================================
563
572inline constexpr auto take_until_or_throw = take_until_fn<true, false>{};
573
574// ============================================================================
575// detail::take_until_and_consume (adaptor instance definition)
576// ============================================================================
577
586inline constexpr auto take_until_and_consume = take_until_fn<false, true>{};
587
588// ============================================================================
589// detail::take_until_or_throw_and_consume (adaptor instance definition)
590// ============================================================================
591
600inline constexpr auto take_until_or_throw_and_consume = take_until_fn<true, true>{};
601
602} // namespace seqan3::detail
Provides seqan3::detail::adaptor_from_functor.
T addressof(T... args)
T begin(T... args)
Provides seqan3::detail::copyable_wrapper.
Provides various transformation traits used by the range module.
T end(T... args)
T forward(T... args)
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.
T move(T... args)
SeqAn specific customisations in the standard namespace.
T operator!=(T... args)
Provides seqan3::detail::transformation_trait_or.
Additional non-standard concepts for ranges.
Hide me