MCRO
C++23 utilities for Unreal Engine.
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages Concepts
Concepts.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/**
15 * @file
16 * @brief
17 * This header exists because STL headers in Android doesn't define STL concepts (other than same_as which is weird),
18 * despite the fact that in `<concepts>` the synopsis contains all of them (but only as a comment).
19 * @file
20 * However, <type_traits> seems to be implemented properly so many concepts here will be just encapsulating equivalent
21 * type traits (like CConstType = std::is_const_v<T>).
22 * Using MSVC implementation as reference
23 */
24
25#if !defined(__cpp_concepts) || !__cpp_concepts
26#error "This code requires C++20 concepts support"
27#endif
28
29#include <type_traits>
30#include "CoreMinimal.h"
31
32#define MCRO_THIS_TYPE decltype(Mcro::Concepts::DecayPtr(this))
33
35{
36 //// Concepts ported from STL
37
38 template<typename A, typename B>
39 concept CSameAs = std::is_same_v<A, B>;
40
41 template<typename A, typename B>
42 concept CSameAsDecayed = std::is_same_v<std::decay_t<A>, std::decay_t<B>>;
43
44 template<typename From, typename To>
45 concept CConvertibleTo = std::is_convertible_v<From, To>;
46
47 template<typename From, typename To>
48 concept CConvertibleToDecayed = std::is_convertible_v<std::decay_t<From>, std::decay_t<To>>;
49
50 template<typename Left, typename Right>
51 concept CAssignableFrom = std::is_assignable_v<Left, Right>;
52
53 template<typename Left, typename Right>
54 concept CAssignableFromDecayed = std::is_assignable_v<std::decay_t<Left>, std::decay_t<Right>>;
55
56 template<typename Derivative, typename Base>
57 concept CDerivedFrom =
58 std::is_base_of_v<
59 std::remove_pointer_t<std::decay_t<Base>>,
60 std::remove_pointer_t<std::decay_t<Derivative>>
61 >
62 && CConvertibleTo<const volatile std::decay_t<Derivative>*, const volatile std::decay_t<Base>*>
63 ;
64
65 template<typename T>
66 concept CIntegral = std::is_integral_v<std::decay_t<T>>;
67
68 template<typename T>
69 concept CSignedIntegral = CIntegral<T> && std::is_signed_v<std::decay_t<T>>;
70
71 template<typename T>
72 concept CUnsignedIntegral = CIntegral<T> && !std::is_signed_v<std::decay_t<T>>;
73
74 template<typename T>
75 concept CFloatingPoint = std::is_floating_point_v<std::decay_t<T>>;
76
77 template<typename T>
79
80 template<typename T>
82 && requires(T&& t)
83 {
84 { !Forward<T>(t) } -> CConvertibleTo<bool>;
85 }
86 ;
87
88 template<typename T>
89 concept CPlainEnum = std::is_enum_v<std::decay_t<T>>;
90
91 template<typename T>
92 concept CScopedEnum = std::is_scoped_enum_v<std::decay_t<T>>;
93
94 template<typename T>
96
97 template<typename A, typename B>
99 requires(const std::remove_reference_t<A>& a, const std::remove_reference_t<B>& b)
100 {
101 { a == b } -> CBooleanTestable;
102 { a != b } -> CBooleanTestable;
103 }
104 ;
105
106 template<typename A, typename B>
108
109 template<typename T>
111
112 template<typename A, typename B>
113 concept CHalfOrdered = requires(const std::remove_reference_t<A>& a, const std::remove_reference_t<B>& b)
114 {
115 { a < b } -> CBooleanTestable;
116 { a > b } -> CBooleanTestable;
117 { a <= b } -> CBooleanTestable;
118 { a >= b } -> CBooleanTestable;
119 };
120
121 template<typename A, typename B>
123
124 template<typename T>
126
127 template<typename T>
128 concept CConstType = std::is_const_v<T>;
129
130 template<typename T>
131 concept CMutableType = !std::is_const_v<T>;
132
133 template<typename T>
134 concept CDestructible = std::is_nothrow_destructible_v<std::decay_t<T>>;
135
136 template<typename T, typename... From>
137 concept CConstructibleFrom = requires(From&&... from) { std::decay_t<T>(Forward<From>(from)...); };
138
139 template<typename T>
140 concept CDefaultInitializable = CConstructibleFrom<T> && requires { std::decay_t<T>{}; };
141
142 template<typename T>
144
145 template<typename T>
150 ;
151
152 template<typename T>
153 concept CMovable = std::is_object_v<T>
157 ;
158
159 template<typename T>
161 && CMovable<T>
165 ;
166
167 template<typename T>
169
170 template<typename T>
172
173 template<typename Function, typename... Args>
174 concept CInvocable = requires(std::decay_t<Function>&& function, Args&&... args)
175 {
176 std::invoke(Forward<std::decay_t<Function>&&>(function), Forward<Args&&>(args)...);
177 };
178
179 template<typename Function, typename... Args>
180 concept CPredicate = CInvocable<Function, Args...> && CBooleanTestable<std::invoke_result_t<Function, Args...>>;
181
182 template<typename Function, typename A, typename B>
183 concept CRelation =
188 ;
189
190 template<typename Function, typename A, typename B>
192
193 template<typename Function, typename A, typename B>
195
196 /** Use Unreal's own Concept templating constraint as a C++20 concept */
197 template<typename Concept, typename... Args>
198 concept CModels = TModels_V<Concept, Args...>;
199
200 //// TSharedPtr, TSharedRef and TWeakPtr concepts
201
202 template<typename T>
204
205 template<typename T>
207
208 template<typename T>
210
211 template<typename T>
213
214 template<typename T>
216
217 template<typename T>
218 concept CSharedFromThis = requires(std::decay_t<T>& t) { { t.AsShared() } -> CSharedRefOrPtr; };
219
220 template<typename T, typename ElementType>
222
223 template<typename T, typename ElementType>
225
226 template<typename T, typename ElementType>
228
229 template<typename T, typename ElementType>
231
232 template<typename T, typename ElementType>
234
235 template<typename T, typename ElementType>
236 concept CSharedFromThisOf = requires(std::decay_t<T>& t) { { t.AsShared() } -> CSharedRefOrPtrOf<ElementType>; };
237
238 //// UClasses constraining concepts
239
240 template<typename T>
242
243 template<typename T>
244 concept CInterface = TIsIInterface<std::decay_t<T>>::Value > 0;
245
246 template<typename T>
248
249 //// String/Text concepts
250
251 template<typename T>
253
254 template<typename T>
256
257 template<typename T>
259
260 template<typename T>
262
263 template<typename T>
264 concept CChar =
265 CSameAs<std::decay_t<T>, UTF8CHAR>
266 || CSameAs<std::decay_t<T>, UTF16CHAR>
267 || CSameAs<std::decay_t<T>, UTF32CHAR>
268 || CSameAs<std::decay_t<T>, WIDECHAR>
269 || CSameAs<std::decay_t<T>, UCS2CHAR>
270 || CSameAs<std::decay_t<T>, ANSICHAR>
271 ;
272
273 template<typename T>
274 concept CNonChar = !CChar<T>;
275
276 template<typename T>
278
279 template<typename T>
281
282 //// Simple objects
283
284 template <typename T>
285 concept CPointer = std::is_pointer_v<std::decay_t<T>>;
286
287 template <typename T>
288 concept CClass = std::is_class_v<std::decay_t<T>>;
289
290 template <typename T>
291 concept CUnion = std::is_union_v<std::decay_t<T>>;
292
293 template <typename T>
295 && !CUObject<T>
297 ;
298
299 //// Is member pointer
300
301 namespace Detail
302 {
303 template<typename T>
305
306 // Given a pointer to a member, extract the class type (e.g. "int MyClass::*" -> Type will contain "MyClass")
307 template<typename ClassType, typename MemberType>
308 struct TExtractClassName<MemberType ClassType::*>
309 {
310 using Type = ClassType;
311 };
312
313 // Given a pointer to a member, extract the class type (e.g. "int MyClass::*" -> returns "MyClass")
314 template<typename MemberPointer>
316 }
317
318 /**
319 * Concept that returns true if the given member pointer belongs to the class.
320 *
321 * Example:
322 * @code
323 * class MyClass
324 * {
325 * int myMember;
326 * };
327 *
328 * CMemberPointerOf<MyClass, &MyClass::myMember>; // --> true;
329 *
330 * class MyOtherClass {};
331 *
332 * CMemberPointerOf<MyOtherClass, &MyClass::myMember>; // --> false;
333 * @endcode
334 */
335 template <typename OwnerObject, typename MemberPointerType>
337 std::is_member_pointer_v<MemberPointerType>
339 ;
340
341 //// Misc
342
343 template <typename T>
344 concept CVoid = std::is_void_v<T>;
345
346 template <typename T>
347 concept CNonVoid = !std::is_void_v<T>;
348
349 template <typename T>
350 concept CRefCounted = requires(std::decay_t<T>& t)
351 {
352 t.AddRef();
353 t.Release();
354 };
355
356 template <typename T>
357 concept CRangeMember = requires(std::decay_t<T>&& t) { t.begin(); t.end(); };
358
359 template <typename T>
360 concept CValidableMember = requires(std::decay_t<T> t) { { t.IsValid() } -> CBooleanTestable; };
361
362 template <typename T>
363 concept CValidableAdl = requires(std::decay_t<T> t) { { IsValid(t) } -> CBooleanTestable; };
364
365 template <typename T>
367
368 /** @brief Attempt to test the input object validity through various methods. */
369 template <CValidable T>
370 bool TestValid(T&& input)
371 {
372 if constexpr (CValidableMember<T>) return input.IsValid();
373 else if constexpr (CValidableAdl<T>) return IsValid(input);
374 else return static_cast<bool>(input);
375 }
376
377 // use in decltype
378 template <typename T> T DecayPtr(T*);
379}
typename TExtractClassName< std::decay_t< MemberPointer > >::Type TExtractedClassName
Definition Concepts.h:315
bool TestValid(T &&input)
Attempt to test the input object validity through various methods.
Definition Concepts.h:370