GCC Code Coverage Report


Directory: ./
File: include/CXXIter/src/Helpers.h
Date: 2023-01-04 16:32:12
Exec Total Coverage
Lines: 16 16 100.0%
Functions: 9 9 100.0%
Branches: 12 18 66.7%

Line Branch Exec Source
1 #pragma once
2
3 #include <tuple>
4 #include <cstdlib>
5 #include <optional>
6 #include <unordered_set>
7
8 /**
9 * @brief Namespace that contains helper functions providing commonly required functionality
10 * when working with CXXIter.
11 * @details These functions are small helper functions that can be used in conjunction with the
12 * iterator API interface. For example by constructing lambdas for reusable tasks
13 * with a clear and concise API, instead of copying the lambda code itself everywhere it is needed.
14 */
15 namespace CXXIter::fn {
16
17 // ################################################################################################
18 // HELPERS
19 // ################################################################################################
20
21 /**
22 * @brief Helper to construct a lambda that extracts the `FIELD_IDX`-th element from tuples or pairs passing through the iterator.
23 * @details You can use this shortcut everywhere, where a lambda is required that takes a tuple or pair and returns their `FIELD_IDX`-th element.
24 * @tparam FIELD_IDX Index of the field to extract.
25 * @return Lambda that, given an arbitrary tuple or pair, extracts its `FIELD_IDX`-th element.
26 *
27 * Usage Example:
28 * - Use the tuple's second (idx = 1) field to sort by.
29 * @code
30 * std::vector<float> input = {1.0f, 2.0f, 0.5f, 3.0f, -42.0f};
31 * std::vector<std::pair<size_t, float>> output = CXXIter::from(input).copied()
32 * .indexed()
33 * .sortBy<CXXIter::DESCENDING>(CXXIter::unzip<1>())
34 * .collect<std::vector>();
35 * // output == {{3, 3.0f}, {1, 2.0f}, {0, 1.0f}, {2, 0.5f}, {4, -42.0f}}
36 * @endcode
37 */
38 template<size_t FIELD_IDX>
39 1 auto unzip() {
40 13 return [](const auto& item) { return std::get<FIELD_IDX>(item); };
41 }
42
43 /**
44 * @brief Helper to construct a filterMap() lambda that takes a pointer, dyncasts it to the requested type and
45 * returns a @c std::optional<TDynCastTarget> with the result if the cast was successfull.
46 * @details You can use this shortcut everywhere, where a lambda is required that takes an element and
47 * returns an @c std::optional<TDynCastTarget> that is set
48 * only when the @c dynamic_cast() was successful.
49 * @tparam TDynCastTarget Type that the elements that are passed to the generated lambda should be cast to.
50 * @return Lambda that attempts to @c dynamic_cast() a given element to @p TDynCastTarget
51 * and returns a @c std::optional<TDynCastTarget> with the result if the
52 * cast was successful. And @c none if it failed.
53 *
54 * Usage Example:
55 * @code
56 * struct Parent {
57 * virtual ~Parent() {}
58 * };
59 * struct Child1 : public Parent {
60 * std::string id;
61 * Child1(const std::string& id) : id(id) {}
62 * };
63 * struct Child2 : public Parent {};
64 *
65 * std::vector<Parent*> input = { new Parent(), new Child1("0"), new Child1("1"), new Child2() };
66 * std::vector<Child1*> output = CXXIter::from(input)
67 * .filterMap(CXXIter::tryDynCast<Child1*>())
68 * .collect<std::vector>();
69 * // output.size() == 2
70 * // output[0]->id == "0"
71 * // output[1]->id == "1"
72 * CXXIter::from(output).forEach([](auto ptr) { delete ptr; });
73 * @endcode
74 */
75 template<typename TDynCastTarget>
76 6 auto tryDynCast() {
77 12 return [](auto itemPtr) -> std::optional<TDynCastTarget> {
78
3/6
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
12 TDynCastTarget targetPtr = dynamic_cast<TDynCastTarget>(itemPtr);
79
6/6
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 2 times.
✓ Branch 6 taken 2 times.
✓ Branch 7 taken 2 times.
12 if(targetPtr == nullptr) { return {}; }
80 6 return targetPtr;
81 6 };
82 }
83
84 /**
85 * @brief Helper that constructs a filter() lambda that filters the iterator elements by calling
86 * the given @p filterExtractFn on each element and then checking whether that is contained in the given
87 * list of @p acceptedValues.
88 * @param filterExtractFn Lambda that extracts a value for each iterator element, which is then searched
89 * for in the list of @p acceptedValues.
90 * @param acceptedValues List of comparison-values that are allowed to remain in the iterator.
91 * @return Lambda that can be passed to CXXIter::filter to filter the iterator elements with a given
92 * list of @p acceptedValues.
93 *
94 * Usage Example:
95 * @code
96 * enum class CakeType { Sacher, ButterCake, CheeseCake, ChocolateCake, StrawberryCake };
97 * struct Cake {
98 * CakeType type;
99 * float volume;
100 * bool operator==(const Cake& o) const { return o.type == type && o.volume == volume; }
101 * };
102 *
103 * std::vector<Cake> input = {
104 * { CakeType::Sacher, 1.33f }, { CakeType::CheeseCake, 5.0f },
105 * { CakeType::ButterCake, 2.33f }, { CakeType::Sacher, 42.0f },
106 * { CakeType::StrawberryCake, 1.6f }, { CakeType::ChocolateCake, 55.0f },
107 * { CakeType::Sacher, 3.63f }, { CakeType::StrawberryCake, 14.0f }
108 * };
109 * std::vector<Cake> output = CXXIter::from(input)
110 * .filter(CXXIter::fn::filterIsOneOf(
111 * [](const Cake& cake) { return cake.type; },
112 * {CakeType::Sacher, CakeType::ChocolateCake}
113 * ))
114 * .collect<std::vector>();
115 * // output == { Cake {CakeType::Sacher, 1.33f}, Cake {CakeType::Sacher, 42.0f},
116 * Cake {CakeType::ChocolateCake, 55.0f}, Cake {CakeType::Sacher, 3.63f} };
117 * @endcode
118 */
119 template<typename TItem, typename TFilterExtractFn>
120 4 auto filterIsOneOf(TFilterExtractFn filterExtractFn, const std::initializer_list<TItem>& acceptedValues) {
121
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
4 std::unordered_set<TItem> _acceptedValues = acceptedValues;
122 20 return [_acceptedValues = std::move(_acceptedValues), filterExtractFn](const auto& item) {
123
2/4
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 8 times.
✗ Branch 7 not taken.
16 return _acceptedValues.contains(filterExtractFn(item));
124 8 };
125 4 }
126
127 /**
128 * @brief Helper that constructs a filter() lambda that filters an iterator's elements by checking whether
129 * they are contained in the given list of @p acceptedValues.
130 * @param acceptedValues List of values that are allowed to remain in the iterator.
131 * @return Lambda that can be passed to CXXIter::filter to filter the iterator elements with a given
132 * list of @p acceptedValues.
133 *
134 * Usage Example:
135 * @code
136 * enum class CakeType { Sacher, ButterCake, CheeseCake, ChocolateCake, StrawberryCake };
137 * struct Cake {
138 * CakeType type;
139 * float volume;
140 * bool operator==(const Cake& o) const { return o.type == type && o.volume == volume; }
141 * };
142 *
143 * std::vector<Cake> input = {
144 * { CakeType::Sacher, 1.33f }, { CakeType::CheeseCake, 5.0f },
145 * { CakeType::ButterCake, 2.33f }, { CakeType::Sacher, 42.0f },
146 * { CakeType::StrawberryCake, 1.6f }, { CakeType::ChocolateCake, 55.0f },
147 * { CakeType::Sacher, 3.63f }, { CakeType::StrawberryCake, 14.0f }
148 * };
149 * std::vector<CakeType> output = CXXIter::from(input)
150 * .map([](const Cake& cake) { return cake.type; })
151 * .filter(CXXIter::fn::filterIsOneOf({CakeType::Sacher, CakeType::ChocolateCake}))
152 * .collect<std::vector>();
153 * // output == {CakeType::Sacher, CakeType::Sacher, CakeType::ChocolateCake, CakeType::Sacher};
154 * @endcode
155 */
156 template<typename TItem>
157 1 auto filterIsOneOf(const std::initializer_list<TItem>& acceptedValues) {
158 9 return filterIsOneOf<TItem>([](const auto& item) { return item; }, acceptedValues);
159 }
160
161 }
162