SeqAn3 3.4.0-rc.1
The Modern C++ library for sequence analysis.
Loading...
Searching...
No Matches
take_exactly_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 <span>
17#include <type_traits>
18
27
28namespace seqan3::detail
29{
30
31// ============================================================================
32// view_take_exactly
33// ============================================================================
34
48template <std::ranges::view urng_t, bool or_throw>
49class view_take_exactly : public std::ranges::view_interface<view_take_exactly<urng_t, or_throw>>
50{
51private:
53 urng_t urange;
54
56 size_t target_size;
57
59 template <bool const_range>
60 class basic_iterator;
61
62private:
67 using iterator = basic_iterator<false>;
73 using const_iterator = basic_iterator<true>;
75
76public:
80 view_take_exactly() = default;
81 view_take_exactly(view_take_exactly const & rhs) = default;
82 view_take_exactly(view_take_exactly && rhs) = default;
83 view_take_exactly & operator=(view_take_exactly const & rhs) = default;
84 view_take_exactly & operator=(view_take_exactly && rhs) = default;
85 ~view_take_exactly() = default;
86
92 constexpr view_take_exactly(urng_t _urange, size_t const _size) : urange{std::move(_urange)}, target_size{_size}
93 {
94 if constexpr (std::ranges::sized_range<urng_t>)
95 {
96 if (std::ranges::size(urange) < target_size)
97 {
98 if constexpr (or_throw)
99 {
101 "You are trying to construct a detail::take_exactly_or_throw from a range that is strictly "
102 "smaller."};
103 }
104 else
105 {
106 target_size = std::ranges::size(urange);
107 }
108 }
109 }
110 }
111
118 template <std::ranges::viewable_range rng_t>
119 requires std::constructible_from<rng_t, std::views::all_t<rng_t>>
120 constexpr view_take_exactly(rng_t && _urange, size_t const _size) :
121 view_take_exactly{std::views::all(std::forward<rng_t>(_urange)), _size}
122 {}
124
141 constexpr auto begin() noexcept
142 {
143 if constexpr (std::ranges::random_access_range<urng_t> && std::ranges::sized_range<urng_t>)
144 return std::ranges::begin(urange);
145 else
146 return iterator{std::ranges::begin(urange), 0, target_size, this};
147 }
148
150 constexpr auto begin() const noexcept
151 requires const_iterable_range<urng_t> && std::ranges::forward_range<urng_t>
152 {
153 if constexpr (std::ranges::random_access_range<urng_t> && std::ranges::sized_range<urng_t>)
154 return std::ranges::cbegin(urange);
155 else
156 {
157 // const_iterator does not work if the underlying iterator is a std::input_iterator (it needs to access
158 // target_size)
159 return const_iterator{std::ranges::begin(urange), 0, target_size};
160 }
161 }
162
176 constexpr auto end() noexcept
177 {
178 if constexpr (std::ranges::random_access_range<urng_t> && std::ranges::sized_range<urng_t>)
179 return std::ranges::begin(urange) + target_size;
180 else
181 return std::ranges::end(urange);
182 }
183
185 constexpr auto end() const noexcept
186 requires const_iterable_range<urng_t> && std::ranges::forward_range<urng_t>
187 {
188 if constexpr (std::ranges::random_access_range<urng_t> && std::ranges::sized_range<urng_t>)
189 return std::ranges::cbegin(urange) + target_size;
190 else
191 return std::ranges::cend(urange);
192 }
194
206 constexpr auto size() const noexcept
207 {
208 return target_size;
209 }
210};
211
214template <typename urng_t, bool or_throw = false>
215view_take_exactly(urng_t &&, size_t) -> view_take_exactly<std::views::all_t<urng_t>, or_throw>;
216
219template <std::ranges::view urng_t, bool or_throw>
220template <bool const_range>
221class view_take_exactly<urng_t, or_throw>::basic_iterator :
222 public inherited_iterator_base<basic_iterator<const_range>, maybe_const_iterator_t<const_range, urng_t>>
223{
224private:
226 using base_base_t = maybe_const_iterator_t<const_range, urng_t>;
228 using base_t = inherited_iterator_base<basic_iterator, maybe_const_iterator_t<const_range, urng_t>>;
229
231 using sentinel_type = maybe_const_sentinel_t<const_range, urng_t>;
232
234 size_t pos{};
235
237 size_t max_pos{};
238
240 std::conditional_t<!std::forward_iterator<base_base_t>, view_take_exactly *, detail::ignore_t> host_ptr;
241
242public:
247 basic_iterator() = default;
248 basic_iterator(basic_iterator const & rhs) = default;
249 basic_iterator(basic_iterator && rhs) = default;
250 basic_iterator & operator=(basic_iterator const & rhs) = default;
251 basic_iterator & operator=(basic_iterator && rhs) = default;
252 ~basic_iterator() = default;
253
255 constexpr basic_iterator(base_base_t it) noexcept(noexcept(base_t{it})) : base_t{std::move(it)}
256 {}
257
259 constexpr basic_iterator(base_base_t it,
260 size_t const _pos,
261 size_t const _max_pos,
262 view_take_exactly * host = nullptr) noexcept(noexcept(base_t{it})) :
263 base_t{std::move(it)},
264 pos{_pos},
265 max_pos(_max_pos)
266 {
267 host_ptr = host;
268
269 if constexpr (!std::forward_iterator<base_base_t>)
270 {
271 assert(host_ptr != nullptr);
272 }
273 }
275
276 using typename base_t::difference_type;
277 using typename base_t::reference;
278
285 constexpr basic_iterator & operator++() noexcept(noexcept(++std::declval<base_t &>()))
286 {
287 base_t::operator++();
288 ++pos;
289 if constexpr (!std::forward_iterator<base_base_t>)
290 --host_ptr->target_size;
291 return *this;
292 }
293
295 constexpr decltype(auto) operator++(int) noexcept(noexcept(++std::declval<basic_iterator &>())
296 && (std::same_as<decltype(std::declval<base_base_t &>()++), void>
297 || std::is_nothrow_copy_constructible_v<basic_iterator>))
298 {
299 // if underlying iterator is a C++20 input iterator (i.e. returns void), return void too.
300 if constexpr (std::same_as<decltype(std::declval<base_base_t &>()++), void>)
301 {
302 ++(*this);
303 }
304 else
305 {
306 basic_iterator cpy{*this};
307 ++(*this);
308 return cpy;
309 }
310 }
311
313 constexpr basic_iterator & operator--() noexcept(noexcept(--std::declval<base_base_t &>()))
314 requires std::bidirectional_iterator<base_base_t>
315 {
316 base_t::operator--();
317 --pos;
318 return *this;
319 }
320
322 constexpr basic_iterator operator--(int) noexcept(noexcept(--std::declval<basic_iterator &>())
323 && std::is_nothrow_copy_constructible_v<basic_iterator>)
324 requires std::bidirectional_iterator<base_base_t>
325 {
326 basic_iterator cpy{*this};
327 --(*this);
328 return cpy;
329 }
330
332 constexpr basic_iterator & operator+=(difference_type const skip) noexcept(noexcept(std::declval<base_t &>() +=
333 skip))
334 requires std::random_access_iterator<base_base_t>
335 {
336 base_t::operator+=(skip);
337 pos += skip;
338 return *this;
339 }
340
342 constexpr basic_iterator & operator-=(difference_type const skip) noexcept(noexcept(std::declval<base_t &>() -=
343 skip))
344 requires std::random_access_iterator<base_base_t>
345 {
346 base_t::operator-=(skip);
347 pos -= skip;
348 return *this;
349 }
351
358 constexpr bool operator==(basic_iterator const & rhs) const
359 noexcept(!or_throw && noexcept(std::declval<base_base_t &>() == std::declval<base_base_t &>()))
360 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,
387 basic_iterator const & rhs) 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))
402 requires std::forward_iterator<base_base_t>
403 {
404 return !(*this == rhs);
405 }
406
408 constexpr friend bool operator!=(sentinel_type const & lhs,
409 basic_iterator const & rhs) noexcept(noexcept(rhs != lhs))
410 {
411 return rhs != lhs;
412 }
414
424 constexpr reference operator[](std::make_unsigned_t<difference_type> const n) const
425 noexcept(noexcept(std::declval<base_base_t &>()[0]))
426 requires std::random_access_iterator<base_base_t>
427 {
428 return base_t::operator[](n);
429 }
431};
432
433// ============================================================================
434// take_fn (adaptor definition)
435// ============================================================================
436
440template <bool or_throw>
441struct take_exactly_fn
442{
444 constexpr auto operator()(size_t const size) const
445 {
446 return adaptor_from_functor{*this, size};
447 }
448
452 template <std::ranges::range urng_t>
453 constexpr auto operator()(urng_t && urange, size_t target_size) const
454 {
455 static_assert(std::ranges::viewable_range<urng_t>,
456 "The views::take adaptor can only be passed viewable_ranges, i.e. Views or &-to-non-View.");
457
458 // safeguard against wrong size
459 if constexpr (std::ranges::sized_range<urng_t>)
460 {
461 if constexpr (or_throw)
462 {
463 if (target_size > std::ranges::size(urange))
464 {
465 throw std::invalid_argument{"You are trying to construct a detail::take_exactly_or_throw from a "
466 "range that is strictly smaller."};
467 }
468 }
469 else
470 {
471 target_size = std::min<size_t>(target_size, std::ranges::size(urange));
472 }
473 }
474
475 // string_view
476 if constexpr (is_type_specialisation_of_v<std::remove_cvref_t<urng_t>, std::basic_string_view>)
477 {
478 // in standard
479 return urange.substr(0, target_size);
480 }
481 // string const &
482 else if constexpr (is_type_specialisation_of_v<std::remove_cvref_t<urng_t>, std::basic_string>
483 && std::is_const_v<std::remove_reference_t<urng_t>>)
484 {
485 // not in standard
486 // seqan3::views::type_reduce does this too
487 return std::basic_string_view{std::ranges::data(urange), target_size};
488 }
489 // contiguous
490 else if constexpr (std::ranges::borrowed_range<urng_t> && std::ranges::contiguous_range<urng_t>
491 && std::ranges::sized_range<urng_t>)
492 {
493 // not in standard (special case for std::span in standard)
494 // seqan3::views::type_reduce does this too
495 return std::span{std::ranges::data(urange), target_size};
496 }
497 // random_access
498 else if constexpr (std::ranges::borrowed_range<urng_t> && std::ranges::random_access_range<urng_t>
499 && std::ranges::sized_range<urng_t>)
500 {
501 // not in standard
502 // seqan3::views::type_reduce does this too
503 return std::ranges::subrange<std::ranges::iterator_t<urng_t>, std::ranges::iterator_t<urng_t>>{
504 std::ranges::begin(urange),
505 std::ranges::begin(urange) + target_size,
506 target_size};
507 }
508 // our type
509 else
510 {
511 return view_take_exactly<std::views::all_t<urng_t>, or_throw>{std::forward<urng_t>(urange), target_size};
512 }
513 }
514};
515
516} // namespace seqan3::detail
517
518// ============================================================================
519// detail::take_exactly (adaptor instance definition)
520// ============================================================================
521
522namespace seqan3::detail
523{
573inline constexpr auto take_exactly = take_exactly_fn<false>{};
574
575// ============================================================================
576// detail::take_exactly_or_throw (adaptor instance definition)
577// ============================================================================
578
587inline constexpr auto take_exactly_or_throw = take_exactly_fn<true>{};
588} // namespace seqan3::detail
Provides seqan3::detail::adaptor_from_functor.
T begin(T... args)
Provides various transformation traits used by the range module.
T end(T... args)
T forward(T... args)
constexpr size_t size
The size of a type pack.
Definition type_pack/traits.hpp:143
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 type traits for working with templates.
Provides seqan3::detail::transformation_trait_or.
Additional non-standard concepts for ranges.
Hide me