MCRO
C++23 utilities for Unreal Engine.
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages Concepts
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 Forward<std::initializer_list<T>>(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, Forward<Ranges>(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, Forward<Ranges>(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 * @param matchOnlyBeginning
110 * By default MatchOrdered returns false for ranges with different lengths
111 */
112 template <CRangeMember Left, CRangeMember Right>
113 requires CConvertibleToDecayed<TRangeElementType<Right>, TRangeElementType<Left>>
114 bool MatchOrdered(Left&& left, Right&& right, bool matchOnlyBeginning = false)
115 {
116 if (IsEmpty(left) && IsEmpty(right)) return true;
117
119 if (!matchOnlyBeginning && size(left) != size(right))
120 return false;
121
122 auto leftIt = left.begin();
123 auto rightIt = right.begin();
124 for (;;)
125 {
126 if (IteratorEquals(leftIt, left.end()) || IteratorEquals(rightIt, right.end()))
127 return matchOnlyBeginning || (
128 IteratorEquals(leftIt, left.end()) && IteratorEquals(rightIt, right.end())
129 );
130
131 if (*leftIt != *rightIt) return false;
132 ++leftIt;
133 ++rightIt;
134 }
135 }
136
137 template <CRangeMember Left, typename Value>
138 requires CConvertibleToDecayed<Value, TRangeElementType<Left>>
139 bool MatchOrdered(Left&& left, std::initializer_list<Value>&& right, bool matchOnlyBeginning = false)
140 {
141 return MatchOrdered(left, right, matchOnlyBeginning);
142 }
143
144 /**
145 * @brief
146 * Return true if input ranges match their values and their order.
147 *
148 * This runs in O(N) time when result is true, unless both input ranges are `CCountableRange`, `matchOnlyBeginning`
149 * is false and the two input ranges have different size.
150 *
151 * @param right Range to compare with
152 * @param matchOnlyBeginning
153 * By default MatchOrdered returns false for ranges with different lengths
154 */
155 template <CRangeMember Right>
156 auto MatchOrdered(Right&& right, bool matchOnlyBeginning = false)
157 {
158 return ranges::make_pipeable([&](auto&& left){ return MatchOrdered(left, right, matchOnlyBeginning); });
159 }
160
161 template <typename Value>
162 auto MatchOrdered(std::initializer_list<Value>&& right, bool matchOnlyBeginning = false)
163 {
164 return ranges::make_pipeable([&](auto&& left){ return MatchOrdered(left, right, matchOnlyBeginning); });
165 }
166
167 template <CFunctionLike Predicate>
168 auto AllOf(Predicate&& pred)
169 {
170 return ranges::make_pipeable([&](auto&& left){ return ranges::all_of(left, pred); });
171 }
172
173 template <CFunctionLike Predicate>
174 auto AnyOf(Predicate&& pred)
175 {
176 return ranges::make_pipeable([&](auto&& left){ return ranges::any_of(left, pred); });
177 }
178
179 FORCEINLINE auto FilterValid()
180 {
181 return ranges::views::filter([]<CValidable T>(T&& item)
182 {
183 return TestValid(Forward<T>(item));
184 });
185 }
186}
Use this header and Start.h in tandem to include third-party library headers which may not tolerate U...
size_t size(TArray< T, A > const &r)
Definition Range.h:98
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:370
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
auto Concat(Ranges &&...right)
pipeable version of ranges::views::concat
Definition Views.h:41
bool MatchOrdered(Left &&left, Right &&right, bool matchOnlyBeginning=false)
Return true if input ranges match their values and their order.
Definition Views.h:114
FORCEINLINE auto IsEmpty()
Check if range is empty.
Definition Views.h:57
auto AnyOf(Predicate &&pred)
Definition Views.h:174
bool IteratorEquals(L const &l, R const &r)
Definition Concepts.h:47
FORCEINLINE auto FilterValid()
Definition Views.h:179
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:163
auto AllOf(Predicate &&pred)
Definition Views.h:168
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