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>
21
30
31namespace seqan3::detail
32{
33
34// ============================================================================
35// view_take_exactly
36// ============================================================================
37
51template <std::ranges::view urng_t, bool or_throw>
52class view_take_exactly : public std::ranges::view_interface<view_take_exactly<urng_t, or_throw>>
53{
54private:
56 urng_t urange;
57
59 size_t target_size;
60
62 template <bool const_range>
63 class basic_iterator;
64
65private:
70 using iterator = basic_iterator<false>;
76 using const_iterator = basic_iterator<true>;
78
79public:
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
218template <typename urng_t,
219 bool or_throw = false>
220view_take_exactly(urng_t && , size_t) -> view_take_exactly<std::views::all_t<urng_t>, or_throw>;
221
224template <std::ranges::view urng_t, bool or_throw>
225template <bool const_range>
226class 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{
229private:
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
247public:
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
444template <bool or_throw>
445struct 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> &&
487 std::is_const_v<std::remove_reference_t<urng_t>>)
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
534namespace seqan3::detail
535{
585inline auto constexpr take_exactly = take_exactly_fn<false>{};
586
587// ============================================================================
588// detail::take_exactly_or_throw (adaptor instance definition)
589// ============================================================================
590
599inline 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)
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.
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.