SeqAn3 3.2.0
The Modern C++ library for sequence analysis.
take_exactly_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 <span>
20#include <type_traits>
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) : urange{std::move(_urange)}, target_size{_size}
96 {
97 if constexpr (std::ranges::sized_range<urng_t>)
98 {
99 if (std::ranges::size(urange) < target_size)
100 {
101 if constexpr (or_throw)
102 {
104 "You are trying to construct a detail::take_exactly_or_throw from a range that is strictly "
105 "smaller."};
106 }
107 else
108 {
109 target_size = std::ranges::size(urange);
110 }
111 }
112 }
113 }
114
121 template <std::ranges::viewable_range rng_t>
122 requires std::constructible_from<rng_t, std::views::all_t<rng_t>>
123 constexpr view_take_exactly(rng_t && _urange, size_t const _size) :
124 view_take_exactly{std::views::all(std::forward<rng_t>(_urange)), _size}
125 {}
127
144 constexpr auto begin() noexcept
145 {
146 if constexpr (std::ranges::random_access_range<urng_t> && std::ranges::sized_range<urng_t>)
147 return std::ranges::begin(urange);
148 else
149 return iterator{std::ranges::begin(urange), 0, target_size, this};
150 }
151
153 constexpr auto begin() const noexcept
154 requires const_iterable_range<urng_t> && std::ranges::forward_range<urng_t>
155 {
156 if constexpr (std::ranges::random_access_range<urng_t> && std::ranges::sized_range<urng_t>)
157 return std::ranges::cbegin(urange);
158 else
159 {
160 // const_iterator does not work if the underlying iterator is a std::input_iterator (it needs to access
161 // target_size)
162 return const_iterator{std::ranges::cbegin(urange), 0, target_size};
163 }
164 }
165
179 constexpr auto end() noexcept
180 {
181 if constexpr (std::ranges::random_access_range<urng_t> && std::ranges::sized_range<urng_t>)
182 return std::ranges::begin(urange) + target_size;
183 else
184 return std::ranges::end(urange);
185 }
186
188 constexpr auto end() const noexcept
189 requires const_iterable_range<urng_t> && std::ranges::forward_range<urng_t>
190 {
191 if constexpr (std::ranges::random_access_range<urng_t> && std::ranges::sized_range<urng_t>)
192 return std::ranges::cbegin(urange) + target_size;
193 else
194 return std::ranges::cend(urange);
195 }
197
209 constexpr auto size() const noexcept
210 {
211 return target_size;
212 }
213};
214
217template <typename urng_t, bool or_throw = false>
218view_take_exactly(urng_t &&, size_t) -> view_take_exactly<std::views::all_t<urng_t>, or_throw>;
219
222template <std::ranges::view urng_t, bool or_throw>
223template <bool const_range>
224class view_take_exactly<urng_t, or_throw>::basic_iterator :
225 public inherited_iterator_base<basic_iterator<const_range>, maybe_const_iterator_t<const_range, urng_t>>
226{
227private:
229 using base_base_t = maybe_const_iterator_t<const_range, urng_t>;
231 using base_t = inherited_iterator_base<basic_iterator, maybe_const_iterator_t<const_range, urng_t>>;
232
234 using sentinel_type = maybe_const_sentinel_t<const_range, urng_t>;
235
237 size_t pos{};
238
240 size_t max_pos{};
241
243 std::conditional_t<!std::forward_iterator<base_base_t>, view_take_exactly *, detail::ignore_t> host_ptr;
244
245public:
250 basic_iterator() = default;
251 basic_iterator(basic_iterator const & rhs) = default;
252 basic_iterator(basic_iterator && rhs) = default;
253 basic_iterator & operator=(basic_iterator const & rhs) = default;
254 basic_iterator & operator=(basic_iterator && rhs) = default;
255 ~basic_iterator() = default;
256
258 constexpr basic_iterator(base_base_t const & it) noexcept(noexcept(base_t{it})) : base_t{std::move(it)}
259 {}
260
262 constexpr basic_iterator(base_base_t it,
263 size_t const _pos,
264 size_t const _max_pos,
265 view_take_exactly * host = nullptr) noexcept(noexcept(base_t{it})) :
266 base_t{std::move(it)},
267 pos{_pos},
268 max_pos(_max_pos)
269 {
270 host_ptr = host;
271
272 if constexpr (!std::forward_iterator<base_base_t>)
273 {
274 assert(host_ptr != nullptr);
275 }
276 }
278
279 using typename base_t::difference_type;
280 using typename base_t::reference;
281
288 constexpr basic_iterator & operator++() noexcept(noexcept(++std::declval<base_t &>()))
289 {
290 base_t::operator++();
291 ++pos;
292 if constexpr (!std::forward_iterator<base_base_t>)
293 --host_ptr->target_size;
294 return *this;
295 }
296
298 constexpr decltype(auto) operator++(int) noexcept(noexcept(++std::declval<basic_iterator &>())
299 && (std::same_as<decltype(std::declval<base_base_t &>()++), void>
300 || std::is_nothrow_copy_constructible_v<basic_iterator>))
301 {
302 // if underlying iterator is a C++20 input iterator (i.e. returns void), return void too.
303 if constexpr (std::same_as<decltype(std::declval<base_base_t &>()++), void>)
304 {
305 ++(*this);
306 }
307 else
308 {
309 basic_iterator cpy{*this};
310 ++(*this);
311 return cpy;
312 }
313 }
314
316 constexpr basic_iterator & operator--() noexcept(noexcept(--std::declval<base_base_t &>()))
317 requires std::bidirectional_iterator<base_base_t>
318 {
319 base_t::operator--();
320 --pos;
321 return *this;
322 }
323
325 constexpr basic_iterator operator--(int) noexcept(noexcept(--std::declval<basic_iterator &>())
326 && std::is_nothrow_copy_constructible_v<basic_iterator>)
327 requires std::bidirectional_iterator<base_base_t>
328 {
329 basic_iterator cpy{*this};
330 --(*this);
331 return cpy;
332 }
333
335 constexpr basic_iterator & operator+=(difference_type const skip) noexcept(noexcept(std::declval<base_t &>() +=
336 skip))
337 requires std::random_access_iterator<base_base_t>
338 {
339 base_t::operator+=(skip);
340 pos += skip;
341 return *this;
342 }
343
345 constexpr basic_iterator & operator-=(difference_type const skip) noexcept(noexcept(std::declval<base_t &>() -=
346 skip))
347 requires std::random_access_iterator<base_base_t>
348 {
349 base_t::operator-=(skip);
350 pos -= skip;
351 return *this;
352 }
354
361 constexpr bool operator==(basic_iterator const & rhs) const
362 noexcept(!or_throw && noexcept(std::declval<base_base_t &>() == std::declval<base_base_t &>()))
363 requires std::forward_iterator<base_base_t>
364 {
365 return this->base() == rhs.base();
366 }
367
369 constexpr bool operator==(sentinel_type const & rhs) const
370 noexcept(!or_throw && noexcept(std::declval<base_base_t const &>() == std::declval<sentinel_type const &>()))
371 {
372 if (pos >= max_pos)
373 return true;
374
375 if (this->base() == rhs)
376 {
377 if constexpr (or_throw)
378 throw unexpected_end_of_input{"Reached end of input before designated size."};
379
380 return true;
381 }
382 else
383 {
384 return false;
385 }
386 }
387
389 constexpr friend bool operator==(sentinel_type const & lhs,
390 basic_iterator const & rhs) noexcept(noexcept(rhs == lhs))
391 {
392 return rhs == lhs;
393 }
394
396 constexpr bool operator!=(sentinel_type const & rhs) const
397 noexcept(noexcept(std::declval<basic_iterator &>() == rhs))
398 {
399 return !(*this == rhs);
400 }
401
403 constexpr bool operator!=(basic_iterator const & rhs) const
404 noexcept(noexcept(std::declval<basic_iterator &>() == rhs))
405 requires std::forward_iterator<base_base_t>
406 {
407 return !(*this == rhs);
408 }
409
411 constexpr friend bool operator!=(sentinel_type const & lhs,
412 basic_iterator const & rhs) noexcept(noexcept(rhs != lhs))
413 {
414 return rhs != lhs;
415 }
417
427 constexpr reference operator[](std::make_unsigned_t<difference_type> const n) const
428 noexcept(noexcept(std::declval<base_base_t &>()[0]))
429 requires std::random_access_iterator<base_base_t>
430 {
431 return base_t::operator[](n);
432 }
434};
435
436// ============================================================================
437// take_fn (adaptor definition)
438// ============================================================================
439
443template <bool or_throw>
444struct take_exactly_fn
445{
447 constexpr auto operator()(size_t const size) const
448 {
449 return adaptor_from_functor{*this, size};
450 }
451
455 template <std::ranges::range urng_t>
456 constexpr auto operator()(urng_t && urange, size_t target_size) const
457 {
458 static_assert(std::ranges::viewable_range<urng_t>,
459 "The views::take adaptor can only be passed viewable_ranges, i.e. Views or &-to-non-View.");
460
461 // safeguard against wrong size
462 if constexpr (std::ranges::sized_range<urng_t>)
463 {
464 if constexpr (or_throw)
465 {
466 if (target_size > std::ranges::size(urange))
467 {
468 throw std::invalid_argument{"You are trying to construct a detail::take_exactly_or_throw from a "
469 "range that is strictly smaller."};
470 }
471 }
472 else
473 {
474 target_size = std::min<size_t>(target_size, std::ranges::size(urange));
475 }
476 }
477
478 // string_view
479 if constexpr (is_type_specialisation_of_v<std::remove_cvref_t<urng_t>, std::basic_string_view>)
480 {
481 // in standard
482 return urange.substr(0, target_size);
483 }
484 // string const &
485 else if constexpr (is_type_specialisation_of_v<std::remove_cvref_t<urng_t>, std::basic_string>
486 && std::is_const_v<std::remove_reference_t<urng_t>>)
487 {
488 // not in standard
489 // seqan3::views::type_reduce does this too
490 return std::basic_string_view{std::ranges::data(urange), target_size};
491 }
492 // contiguous
493 else if constexpr (std::ranges::borrowed_range<urng_t> && std::ranges::contiguous_range<urng_t>
494 && std::ranges::sized_range<urng_t>)
495 {
496 // not in standard (special case for std::span in standard)
497 // seqan3::views::type_reduce does this too
498 return std::span{std::ranges::data(urange), target_size};
499 }
500 // random_access
501 else if constexpr (std::ranges::borrowed_range<urng_t> && std::ranges::random_access_range<urng_t>
502 && std::ranges::sized_range<urng_t>)
503 {
504 // not in standard
505 // seqan3::views::type_reduce does this too
506 return std::ranges::subrange<std::ranges::iterator_t<urng_t>, std::ranges::iterator_t<urng_t>>{
507 std::ranges::begin(urange),
508 std::ranges::begin(urange) + target_size,
509 target_size};
510 }
511 // our type
512 else
513 {
514 return view_take_exactly<std::views::all_t<urng_t>, or_throw>{std::forward<urng_t>(urange), target_size};
515 }
516 }
517};
518
519} // namespace seqan3::detail
520
521// ============================================================================
522// detail::take_exactly (adaptor instance definition)
523// ============================================================================
524
525namespace seqan3::detail
526{
576inline constexpr auto take_exactly = take_exactly_fn<false>{};
577
578// ============================================================================
579// detail::take_exactly_or_throw (adaptor instance definition)
580// ============================================================================
581
590inline constexpr auto take_exactly_or_throw = take_exactly_fn<true>{};
591} // 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: traits.hpp:146
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.