MCRO
C++23 utilities for Unreal Engine.
Loading...
Searching...
No Matches
Views.h
Go to the documentation of this file.
1/** @noop License Comment
2 * @file
3 * @copyright
4 * This Source Code is subject to the terms of the Mozilla Public License, v2.0.
5 * If a copy of the MPL was not distributed with this file You can obtain one at
6 * https://mozilla.org/MPL/2.0/
7 *
8 * @author David Mórász
9 * @date 2025
10 */
11
12#pragma once
13
14#include "CoreMinimal.h"
16
18#include "range/v3/all.hpp"
20
21namespace Mcro::Range
22{
23 using namespace Mcro::Concepts;
24
25 /** @brief Make an initializer list compatible with range API's */
26 template <typename T>
27 decltype(auto) Literal(std::initializer_list<T>&& input) { return FWD(input); }
28
29 /** @brief pipeable version of `ranges::views::zip` */
30 template <CRangeMember... Ranges>
31 auto Zip(Ranges&&...right)
32 {
33 return ranges::make_pipeable([&](auto&& left)
34 {
35 return ranges::views::zip(left, FWD(right)...);
36 });
37 }
38
39 /** @brief pipeable version of `ranges::views::concat` */
40 template <CRangeMember... Ranges>
41 auto Concat(Ranges&&...right)
42 {
43 return ranges::make_pipeable([&](auto&& left)
44 {
45 return ranges::views::concat(left, FWD(right)...);
46 });
47 }
48
49 /** @brief Check if range is empty */
50 template <CRangeMember Input>
51 bool IsEmpty(Input&& range)
52 {
53 return IteratorEquals(range.begin(), range.end());
54 }
55
56 /** @brief Check if range is empty */
57 FORCEINLINE auto IsEmpty()
58 {
59 return ranges::make_pipeable([](auto&& left){ return IsEmpty(left); });
60 }
61
62 /** @brief Get's the first element of a range or return a provided default value. Same as `*r.begin()` but safer. */
63 template <
64 CRangeMember Input,
65 typename Value = TRangeElementType<Input>
66 >
67 decltype(auto) First(Input&& range, Value&& def)
68 {
69 return IsEmpty(range) ? def : *range.begin();
70 }
71
72 /** @brief Get's the first element of a range or return a provided default value. Same as `*r.begin()` but safer. */
73 FORCEINLINE auto First(auto&& def)
74 {
75 return ranges::make_pipeable([&](auto&& left){ return First(left, def); });
76 }
77
78 /**
79 * @brief
80 * Get's the first element of a range or return the default value for the element type. Same as `*r.begin()` but safer.
81 */
82 template <
83 CRangeMember Input,
84 typename Value = TRangeElementType<Input>
85 >
86 decltype(auto) FirstOrDefault(Input&& range)
87 {
88 return IsEmpty(range) ? Value{} : *range.begin();
89 }
90
91 /**
92 * @brief
93 * Get's the first element of a range or return the default value for the element type. Same as `*r.begin()` but safer.
94 */
95 FORCEINLINE auto FirstOrDefault()
96 {
97 return ranges::make_pipeable([&](auto&& left){ return FirstOrDefault(left); });
98 }
99
100 /**
101 * @brief
102 * Return true if input ranges match their values and their order.
103 *
104 * This runs in O(N) time when result is true, unless both input ranges are `CCountableRange`, `matchOnlyBeginning`
105 * is false and the two input ranges have different size.
106 *
107 * @param left Input range
108 * @param right Range to compare with
109 *
110 * @param matchOnlyBeginning
111 * By default MatchOrdered returns false for ranges with different lengths
112 */
113 template <CRangeMember Left, CRangeMember Right>
114 requires CCoreHalfEqualityComparable<TRangeElementType<Left>, TRangeElementType<Right>>
115 bool MatchOrdered(Left&& left, Right&& right, bool matchOnlyBeginning = false)
116 {
117 if (IsEmpty(left) && IsEmpty(right)) return true;
118
120 if (!matchOnlyBeginning && size(left) != size(right))
121 return false;
122
123 auto leftIt = left.begin();
124 auto rightIt = right.begin();
125 for (;;)
126 {
127 if (IteratorEquals(leftIt, left.end()) || IteratorEquals(rightIt, right.end()))
128 return matchOnlyBeginning || (
129 IteratorEquals(leftIt, left.end()) && IteratorEquals(rightIt, right.end())
130 );
131
132 if (*leftIt != *rightIt) return false;
133 ++leftIt;
134 ++rightIt;
135 }
136 }
137
138 template <CRangeMember Left, typename Value>
139 requires CCoreHalfEqualityComparable<Value, TRangeElementType<Left>>
140 bool MatchOrdered(Left&& left, std::initializer_list<Value>&& right, bool matchOnlyBeginning = false)
141 {
142 return MatchOrdered(left, right, matchOnlyBeginning);
143 }
144
145 /**
146 * @brief
147 * Return true if input ranges match their values and their order.
148 *
149 * This runs in O(N) time when result is true, unless both input ranges are `CCountableRange`, `matchOnlyBeginning`
150 * is false and the two input ranges have different size.
151 *
152 * @param right Range to compare with
153 *
154 * @param matchOnlyBeginning
155 * By default MatchOrdered returns false for ranges with different lengths
156 */
157 template <CRangeMember Right>
158 auto MatchOrdered(Right&& right, bool matchOnlyBeginning = false)
159 {
160 return ranges::make_pipeable([&](auto&& left){ return MatchOrdered(left, right, matchOnlyBeginning); });
161 }
162
163 template <typename Value>
164 auto MatchOrdered(std::initializer_list<Value>&& right, bool matchOnlyBeginning = false)
165 {
166 return ranges::make_pipeable([&](auto&& left){ return MatchOrdered(left, right, matchOnlyBeginning); });
167 }
168
169 template <CFunctionLike Predicate>
170 auto AllOf(Predicate&& pred)
171 {
172 return ranges::make_pipeable([&](auto&& left){ return ranges::all_of(left, pred); });
173 }
174
175 template <CFunctionLike Predicate>
176 auto AnyOf(Predicate&& pred)
177 {
178 return ranges::make_pipeable([&](auto&& left){ return ranges::any_of(left, pred); });
179 }
180
181 FORCEINLINE auto FilterValid()
182 {
183 return ranges::views::filter([]<CValidable T>(T&& item)
184 {
185 return TestValid(FWD(item));
186 });
187 }
188
189 /**
190 * @brief
191 * Transform a range of tuples with structured binding function arguments, so range transformations shouldn't
192 * bother with the actual type of the tuple.
193 *
194 * For example:
195 * @code
196 * int32 size = 4;
197 * // | highFreq | lowFreq
198 * namespace rv = ranges::views; // V V
199 * auto things = rv::cartesian(rv::indices(0, size), rv::indices(0, size))
200 * | TransformTuple([size](int32 highFreq, int32 lowFreq)
201 * {
202 * return lowFreq * size + highFreq;
203 * })
204 * ;
205 * FMT_LOG(LogTemp, Display, "Things: {0}", things);
206 * //-> Things: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
207 * @endcode
208 *
209 * @tparam Transform A transformation function type with signature compatible with input tuple.
210 * @tparam Left The type of the range of tuples
211 * @param left The range of tuples
212 * @param tr A transformation function with signature compatible with input tuple.
213 * @return Range with transformed elements
214 */
215 template <CFunctionLike Transform, CRangeOfTuplesCompatibleWithFunction<Transform> Left>
216 auto TransformTuple(Left&& left, Transform&& tr)
217 {
218 using Tuple = TRangeElementType<Left>;
219 return ranges::views::transform(FWD(left), [tr](Tuple const& tuple)
220 {
221 return InvokeWithTuple(tr, tuple);
222 });
223 }
224
225 /**
226 * @brief
227 * Transform a range of tuples with structured binding function arguments, so range transformations shouldn't
228 * bother with the actual type of the tuple.
229 *
230 * For example:
231 * @code
232 * int32 size = 4;
233 * // | highFreq | lowFreq
234 * namespace rv = ranges::views; // V V
235 * auto things = rv::cartesian(rv::indices(0, size), rv::indices(0, size))
236 * | TransformTuple([size](int32 highFreq, int32 lowFreq)
237 * {
238 * return lowFreq * size + highFreq;
239 * })
240 * ;
241 * FMT_LOG(LogTemp, Display, "Things: {0}", things);
242 * //-> Things: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
243 * @endcode
244 *
245 * @tparam Transform A transformation function type with signature compatible with input tuple.
246 * @param tr A transformation function with signature compatible with input tuple.
247 * @return Range with transformed elements
248 */
249 template <CFunctionLike Transform>
250 auto TransformTuple(Transform&& tr)
251 {
252 return ranges::make_pipeable([tr]<CRangeOfTuplesCompatibleWithFunction<Transform> Left>(Left&& left)
253 {
254 return TransformTuple(FWD(left), tr);
255 });
256 }
257
258 /**
259 * @brief
260 * Filter a range of tuples with structured binding function arguments, so filter predicates shouldn't
261 * bother with the actual type of the tuple.
262 *
263 * For example:
264 * @code
265 * int32 size = 4;
266 * // | highFreq | lowFreq
267 * namespace rv = ranges::views; // V V
268 * auto things = rv::cartesian(rv::indices(0, size), rv::indices(0, size))
269 * | FilterTuple([size](int32 highFreq, int32 lowFreq)
270 * {
271 * return highFreq == 2;
272 * })
273 * ;
274 * FMT_LOG(LogTemp, Display, "Things: {0}", things);
275 * //-> Things: [(2, 0), (2, 1), (2, 2), (2, 3)]
276 * @endcode
277 *
278 * @tparam Predicate A transformation function type with signature compatible with input tuple.
279 * @tparam Left The type of the range of tuples
280 * @param left The range of tuples
281 * @param predicate A transformation function with signature compatible with input tuple.
282 * @return Range with transformed elements
283 */
284 template <CFunctionLike Predicate, CRangeOfTuplesCompatibleWithFunction<Predicate> Left>
285 auto FilterTuple(Left&& left, Predicate&& predicate)
286 {
287 using Tuple = TRangeElementType<Left>;
288 return ranges::views::transform(FWD(left), [predicate](Tuple const& tuple)
289 {
290 return InvokeWithTuple(predicate, tuple);
291 });
292 }
293
294 /**
295 * @brief
296 * Filter a range of tuples with structured binding function arguments, so filter predicates shouldn't
297 * bother with the actual type of the tuple.
298 *
299 * For example:
300 * @code
301 * int32 size = 4;
302 * // | highFreq | lowFreq
303 * namespace rv = ranges::views; // V V
304 * auto things = rv::cartesian(rv::indices(0, size), rv::indices(0, size))
305 * | FilterTuple([size](int32 highFreq, int32 lowFreq)
306 * {
307 * return highFreq == 2;
308 * })
309 * ;
310 * FMT_LOG(LogTemp, Display, "Things: {0}", things);
311 * //-> Things: [(2, 0), (2, 1), (2, 2), (2, 3)]
312 * @endcode
313 *
314 * @tparam Predicate A filter predicate function type with signature compatible with input tuple.
315 * @param predicate A filter predicate function with signature compatible with input tuple.
316 * @return Range with transformed elements
317 */
318 template <CFunctionLike Predicate>
319 auto FilterTuple(Predicate&& predicate)
320 {
321 return ranges::make_pipeable([predicate]<CRangeOfTuplesCompatibleWithFunction<Predicate> Left>(Left&& left)
322 {
323 return FilterTuple(FWD(left), predicate);
324 });
325 }
326
327 template <size_t ItemIndex, CRangeOfTuples Range>
328 auto SelectTupleItem(Range&& left)
329 {
330 using Tuple = TRangeElementType<Range>;
331 return ranges::views::transform(FWD(left), [](Tuple const& tuple)
332 {
333 return GetItem<ItemIndex>(tuple);
334 });
335 }
336
337 template <size_t ItemIndex>
339 {
340 return ranges::make_pipeable([]<CRangeOfTuples Left>(Left&& left)
341 {
342 return SelectTupleItem<ItemIndex>(FWD(left));
343 });
344 }
345
346 FORCEINLINE auto GetKeys() { return SelectTupleItem<0>(); }
347 FORCEINLINE auto GetValues() { return SelectTupleItem<1>(); }
348}
Use this header and Start.h in tandem to include third-party library headers which may not tolerate U...
#define FWD(...)
Shorten forwarding expression with this macro so one may not need to specify explicit type.
Definition Macros.h:100
size_t size(TArray< T, A > const &r)
Definition Range.h:101
Use this header and End.h in tandem to include third-party library headers which may not tolerate Unr...
bool TestValid(T &&input)
Attempt to test the input object validity through various methods.
Definition Concepts.h:382
TFunction_Return< Function > InvokeWithTuple(Function &&function, Tuple &&arguments)
A clone of std::apply for Unreal, STL and RangeV3 tuples which also supports function pointers.
decltype(auto) First(Input &&range, Value &&def)
Get's the first element of a range or return a provided default value. Same as *r....
Definition Views.h:67
bool MatchOrdered(Left &&left, Right &&right, bool matchOnlyBeginning=false)
Return true if input ranges match their values and their order.
Definition Views.h:115
auto Concat(Ranges &&...right)
pipeable version of ranges::views::concat
Definition Views.h:41
FORCEINLINE auto GetKeys()
Definition Views.h:346
FORCEINLINE auto GetValues()
Definition Views.h:347
auto SelectTupleItem()
Definition Views.h:338
FORCEINLINE auto IsEmpty()
Check if range is empty.
Definition Views.h:57
auto AnyOf(Predicate &&pred)
Definition Views.h:176
bool IteratorEquals(L const &l, R const &r)
Definition Concepts.h:45
FORCEINLINE auto FilterValid()
Definition Views.h:181
auto Zip(Ranges &&...right)
pipeable version of ranges::views::zip
Definition Views.h:31
TIteratorElementType< decltype(DeclVal< T >().begin())> TRangeElementType
return a range's associated content type determined by dereferencing their iterator.
Definition Concepts.h:161
auto AllOf(Predicate &&pred)
Definition Views.h:170
auto TransformTuple(Left &&left, Transform &&tr)
Transform a range of tuples with structured binding function arguments, so range transformations shou...
Definition Views.h:216
FORCEINLINE auto FirstOrDefault()
Get's the first element of a range or return the default value for the element type....
Definition Views.h:95
decltype(auto) Literal(std::initializer_list< T > &&input)
Make an initializer list compatible with range API's.
Definition Views.h:27
auto FilterTuple(Left &&left, Predicate &&predicate)
Filter a range of tuples with structured binding function arguments, so filter predicates shouldn't b...
Definition Views.h:285
decltype(auto) GetItem(T &&tuple)
Definition Tuples.h:106