GCC Code Coverage Report


Directory: gridformat/
File: gridformat/common/filtered_range.hpp
Date: 2025-03-26 17:08:15
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 8380 explicit Iterator(IT it, Sentinel sentinel, const Predicate& pred)
38 8380 : _it{it}
39
1/2
✓ Branch 1 taken 257 times.
✗ Branch 2 not taken.
8236 , _sentinel{sentinel}
40 8380 , _pred{&pred} {
41
4/4
✓ Branch 1 taken 2942 times.
✓ Branch 2 taken 1248 times.
✓ Branch 3 taken 181 times.
✓ Branch 4 taken 76 times.
8380 if (_should_increment())
42
1/2
✓ Branch 1 taken 181 times.
✗ Branch 2 not taken.
5732 _increment();
43 8380 }
44
45 51348 friend bool operator==(const Iterator& self,
46 const std::default_sentinel_t&) noexcept {
47 51348 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 26160 decltype(auto) _dereference() const {
59
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 13080 times.
26160 assert(!_is_end());
60 26160 return *_it;
61 }
62
63 48280 void _increment() {
64
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 24140 times.
48280 assert(!_is_end());
65 48280 ++_it;
66
2/2
✓ Branch 1 taken 55606 times.
✓ Branch 2 taken 24140 times.
159492 while (_should_increment())
67 111212 ++_it;
68 48280 }
69
70 bool _is_equal(const Iterator& other) const {
71 return _it == other._it;
72 }
73
74 242312 bool _is_end() const {
75 242312 return _it == _sentinel;
76 }
77
78 159898 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.
159898 return (*_pred)(*_it);
80 }
81
82 167872 bool _should_increment() const {
83
2/2
✓ Branch 1 taken 79949 times.
✓ Branch 2 taken 3987 times.
167872 if (!_is_end())
84 159898 return !_current_true();
85 7974 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 1980 explicit FilteredRange(_R&& range, Predicate&& pred)
117 1980 : _range{std::forward<_R>(range)}
118 1980 , _pred{std::move(pred)}
119 1980 {}
120
121 8380 auto begin() const {
122 return FilteredRangeDetail::Iterator{
123
1/2
✓ Branch 1 taken 530 times.
✗ Branch 2 not taken.
8970 std::ranges::begin(_range),
124
1/2
✓ Branch 1 taken 384 times.
✗ Branch 2 not taken.
9140 std::ranges::end(_range),
125 8380 _pred,
126
2/4
✓ Branch 1 taken 530 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 76 times.
✗ Branch 5 not taken.
9224 };
127 }
128
129 8380 auto end() const {
130 8380 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 990 auto filter_by(P&& predicate, R&& range) {
147 990 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