GCC Code Coverage Report


Directory: gridformat/
File: gridformat/common/filtered_range.hpp
Date: 2024-11-10 16:24:00
Exec Total Coverage
Lines: 39 39 100.0%
Functions: 554 573 96.7%
Branches: 19 29 65.5%

Line Branch Exec Source
1 // SPDX-FileCopyrightText: 2022-2023 Dennis Gläser <dennis.glaeser@iws.uni-stuttgart.de>
2 // SPDX-License-Identifier: MIT
3 /*!
4 * \file
5 * \ingroup Common
6 * \copydoc GridFormat::FilteredRange
7 */
8 #ifndef GRIDFORMAT_COMMON_FILTERED_RANGE_HPP_
9 #define GRIDFORMAT_COMMON_FILTERED_RANGE_HPP_
10
11 #include <ranges>
12 #include <cassert>
13 #include <utility>
14 #include <concepts>
15 #include <iterator>
16
17 #include <gridformat/common/iterator_facades.hpp>
18
19 namespace GridFormat {
20
21 #ifndef DOXYGEN
22 namespace FilteredRangeDetail {
23
24 template<typename Range, typename Predicate>
25 using PredicateResult = std::invoke_result_t<
26 const Predicate&,
27 std::ranges::range_reference_t<Range>
28 >;
29
30 template<typename IT, typename Sentinel, typename Predicate>
31 class Iterator
32 : public ForwardIteratorFacade<Iterator<IT, Sentinel, Predicate>,
33 typename std::iterator_traits<IT>::value_type,
34 typename std::iterator_traits<IT>::reference> {
35 public:
36 Iterator() = default;
37 8304 explicit Iterator(IT it, Sentinel sentinel, const Predicate& pred)
38 8304 : _it{it}
39
1/2
✓ Branch 1 taken 257 times.
✗ Branch 2 not taken.
8160 , _sentinel{sentinel}
40 8304 , _pred{&pred} {
41
4/4
✓ Branch 1 taken 2916 times.
✓ Branch 2 taken 1236 times.
✓ Branch 3 taken 181 times.
✓ Branch 4 taken 76 times.
8304 if (_should_increment())
42
1/2
✓ Branch 1 taken 181 times.
✗ Branch 2 not taken.
5680 _increment();
43 8304 }
44
45 51072 friend bool operator==(const Iterator& self,
46 const std::default_sentinel_t&) noexcept {
47 51072 return self._it == self._sentinel;
48 }
49
50 friend bool operator==(const std::default_sentinel_t& s,
51 const Iterator& self) noexcept {
52 return self == s;
53 }
54
55 private:
56 friend IteratorAccess;
57
58 26040 decltype(auto) _dereference() const {
59
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 13020 times.
26040 assert(!_is_end());
60 26040 return *_it;
61 }
62
63 48028 void _increment() {
64
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 24014 times.
48028 assert(!_is_end());
65 48028 ++_it;
66
2/2
✓ Branch 1 taken 55372 times.
✓ Branch 2 taken 24014 times.
158772 while (_should_increment())
67 110744 ++_it;
68 48028 }
69
70 bool _is_equal(const Iterator& other) const {
71 return _it == other._it;
72 }
73
74 241144 bool _is_end() const {
75 241144 return _it == _sentinel;
76 }
77
78 159174 bool _current_true() const {
79
3/5
✓ Branch 1 taken 49483 times.
✓ Branch 2 taken 4593 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 49483 times.
✗ Branch 5 not taken.
159174 return (*_pred)(*_it);
80 }
81
82 167076 bool _should_increment() const {
83
2/2
✓ Branch 1 taken 79587 times.
✓ Branch 2 taken 3951 times.
167076 if (!_is_end())
84 159174 return !_current_true();
85 7902 return false;
86 }
87
88 IT _it;
89 Sentinel _sentinel;
90 const Predicate* _pred{nullptr};
91 };
92
93 template<typename I, typename S, typename P>
94 Iterator(I&&, S&&, const P&) -> Iterator<std::remove_cvref_t<I>, std::remove_cvref_t<S>, P>;
95
96 } // namespace FilteredRangeDetail
97 #endif // DOXYGEN
98
99 /*!
100 * \ingroup Common
101 * \brief Filters a range by a given predicate and
102 * only yields those elements that fulfill it.
103 * \note We need this because `std::views::filter` yields a view that
104 * exposes `begin()` and `end()` only as non-const (because the
105 * filtered begin iterator is cached in the first call to `begin()`).
106 * However, we need to be able to use const filtered ranges in several contexts,
107 * at the cost of finding the beginning of the range every time the filtered
108 * range is traversed.
109 */
110 template<std::ranges::forward_range R,
111 std::invocable<std::ranges::range_reference_t<R>> Predicate>
112 requires(std::convertible_to<bool, FilteredRangeDetail::PredicateResult<R, Predicate>>)
113 class FilteredRange {
114 public:
115 template<typename _R> requires(std::convertible_to<_R, R>)
116 1964 explicit FilteredRange(_R&& range, Predicate&& pred)
117 1964 : _range{std::forward<_R>(range)}
118 1964 , _pred{std::move(pred)}
119 1964 {}
120
121 8304 auto begin() const {
122 return FilteredRangeDetail::Iterator{
123
1/2
✓ Branch 1 taken 530 times.
✗ Branch 2 not taken.
8894 std::ranges::begin(_range),
124
1/2
✓ Branch 1 taken 384 times.
✗ Branch 2 not taken.
9064 std::ranges::end(_range),
125 8304 _pred,
126
2/4
✓ Branch 1 taken 530 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 76 times.
✗ Branch 5 not taken.
9148 };
127 }
128
129 8304 auto end() const {
130 8304 return std::default_sentinel_t{};
131 }
132
133 private:
134 R _range;
135 std::remove_cvref_t<Predicate> _pred;
136 };
137
138 template<std::ranges::range R, typename P> requires(!std::is_lvalue_reference_v<R>)
139 FilteredRange(R&&, P&&) -> FilteredRange<std::remove_cvref_t<R>, std::remove_cvref_t<P>>;
140 template<std::ranges::range R, typename P> requires(std::is_lvalue_reference_v<R>)
141 FilteredRange(R&&, P&&) -> FilteredRange<std::remove_reference_t<R>&, std::remove_cvref_t<P>>;
142
143 namespace Ranges {
144
145 template<typename P, std::ranges::range R>
146 982 auto filter_by(P&& predicate, R&& range) {
147 982 return FilteredRange{std::forward<R>(range), std::forward<P>(predicate)};
148 }
149
150 } // namespace Ranges
151 } // namespace GridFormat
152
153 #endif // GRIDFORMAT_COMMON_FILTERED_RANGE_HPP_
154