MCRO
C++23 utilities for Unreal Engine.
Loading...
Searching...
No Matches
Any.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"
15#include "Mcro/Ansi/Allocator.h"
16#include "Mcro/Ansi/New.h"
17#include "Mcro/TypeName.h"
18#include "Mcro/TextMacros.h"
19#include "Mcro/Templates.h"
20#include "Mcro/FunctionTraits.h"
21#include "Mcro/TypeInfo.h"
22
23namespace Mcro::Any
24{
25 using namespace Mcro::TypeName;
26 using namespace Mcro::TypeInfo;
27 using namespace Mcro::Templates;
28 using namespace Mcro::FunctionTraits;
29 using namespace Mcro::Inheritance;
30
31 struct FAny;
32
33 /**
34 * @brief
35 * Give the opportunity to customize object lifespan operations for `FAny` by either specializing this template
36 * or just providing functors in-place
37 *
38 * @tparam T The type being set for an FAny
39 */
40 template <typename T>
42 {
43 TFunction<void(T*)> Destruct {[](T* object) { delete object; }};
44 TFunction<T*(T const&)> CopyConstruct {[](T const& object)
45 {
46 if constexpr (CCopyConstructible<T>) return new T(object);
47 else return nullptr;
48 }};
49 };
50
51 /** @brief Type facilities for `FAny` enforcing standard memory allocations */
52 template <typename T>
54 .Destruct = [](T* object) { Ansi::Delete(object); },
55 .CopyConstruct = [](T const& object)
56 {
57 if constexpr (CCopyConstructible<T>) return Ansi::New<T>(object);
58 else return nullptr;
59 }
60 };
61
62 /**
63 * @brief
64 * A simplistic but type-safe and RAII compliant storage for anything. Enclosed data is owned by this type.
65 *
66 * Use this with care, the underlying data can be only accessed with the same type as it has been constructed with,
67 * or with types provided by `ValidAs`. This means derived classes cannot be accessed with their base types safely
68 * and implicitly. MCRO however provides methods for classes to allow them exposing base types to FAny (and
69 * other facilities):
70 * @code
71 * class FMyThing : public TInherit<IFoo, IBar, IEtc>
72 * {
73 * // ...
74 * }
75 * @endcode
76 * `TInherit` has a member alias `using Bases = TTypes<...>` and that can be used by FAny to automatically register
77 * base classes as compatible ones.
78 *
79 * Enclosed value is recommended to be copy constructible. It may yield a runtime error otherwise. Moving an FAny
80 * will just transfer ownership of the wrapped object but will not move construct a new object. The source FAny
81 * will be reset to an invalid state.
82 *
83 * @todo
84 * C++ 26 has promising proposal for static value-based reflection, which can gather metadata from classes
85 * or even emit them. The best summary I found so far is a stack-overflow answer https://stackoverflow.com/a/77477029
86 * Once that's available we can gather base classes in compile time, and do dynamic casting of objects without
87 * the need for intrusive extra syntax, or extra work at construction.
88 * Currently GCC's `__bases` would be perfect for the job, but other popular compilers don't have similar
89 * intrinsics. Once such a feature becomes widely available base classes can be automatically added as aliases for
90 * types wrapped in FAny.
91 */
92 struct MCRO_API FAny
93 {
94 template <typename T>
95 FAny(T* newObject, TAnyTypeFacilities<T> const& facilities = {})
96 : Storage(newObject)
97 , MainType(TTypeOf<T>)
98 , Destruct([facilities](FAny* self)
99 {
100 T* object = static_cast<T*>(self->Storage);
101 facilities.Destruct(object);
102 self->Storage = nullptr;
103 })
104 , CopyConstruct([facilities](FAny* self, FAny const& other)
105 {
106 const T* object = static_cast<const T*>(other.Storage);
107 self->Storage = facilities.CopyConstruct(*object);
108 checkf(self->Storage, TEXT_"Copy constructor failed for %s. Is it deleted?", *TTypeString<T>());
109
110 CopyTypeInfo(self, &other);
111 })
112 {
113 ValidTypes.Add(MainType);
114
115 if constexpr (CHasBases<T>)
116 {
117 ForEachExplicitBase<T>([this] <typename Base> ()
118 {
119 AddAlias(TTypeOf<Base>);
120 });
121 }
122 }
123
124 FORCEINLINE FAny() {}
125 FAny(FAny const& other);
126 FAny(FAny&& other);
128
129 template <typename T>
130 const T* TryGet() const
131 {
132 return ValidTypes.Contains(TTypeOf<T>)
133 ? static_cast<const T*>(Storage)
134 : nullptr;
135 }
136
137 template <typename T>
139 {
140 return ValidTypes.Contains(TTypeOf<T>)
141 ? static_cast<T*>(Storage)
142 : nullptr;
143 }
144
145 /** @brief Specify one type the enclosed value can be safely cast to, and is valid to be used with `TryGet`. */
146 template <typename T, typename Self>
147 decltype(auto) WithAlias(this Self&& self)
148 {
149 self.AddAlias(TTypeOf<T>);
150
151 if constexpr (CHasBases<T>)
152 {
153 ForEachExplicitBase<T>([&] <typename Base> ()
154 {
155 self.AddAlias(TTypeOf<Base>);
156 });
157 }
158 return FWD(self);
159 }
160
161 /** @brief Specify multiple types the enclosed value can be safely cast to, and are valid to be used with `TryGet`. */
162 template <typename Self, typename... T>
163 decltype(auto) With(this Self&& self, TTypes<T...>&&)
164 {
165 (self.AddAlias(TTypeOf<T>), ...);
166 return FWD(self);
167 }
168
169 FORCEINLINE bool IsValid() const { return static_cast<bool>(Storage); }
170 FORCEINLINE FType GetType() const { return MainType; }
171 FORCEINLINE TSet<FType> const& GetValidTypes() const { return ValidTypes; }
172
173 private:
174 void AddAlias(FType const& alias);
175 static void CopyTypeInfo(FAny* self, const FAny* other);
176 void Reset();
177
178 void* Storage = nullptr;
179 FType MainType {};
180
181 TFunction<void(FAny* self)> Destruct {};
182 TFunction<void(FAny* self, FAny const& other)> CopyConstruct {};
183
184 TSet<FType> ValidTypes {};
185 };
186}
#define FWD(...)
Shorten forwarding expression with this macro so one may not need to specify explicit type.
Definition Macros.h:100
Use leading TEXT_ without parenthesis for Unreal compatible text literals.
#define TEXT_
A convenience alternative to Unreal's own TEXT macro but this one doesn't require parenthesis around ...
Definition TextMacros.h:53
Convert types to string.
void Delete(T *ptr)
Force using the ANSI memory release behavior, instead of the Unreal default.
Definition New.h:49
T * New(Args &&... args)
Force using the ANSI memory allocation behavior, instead of the Unreal default.
Definition New.h:32
TAnyTypeFacilities< T > AnsiAnyFacilities
Type facilities for FAny enforcing standard memory allocations.
Definition Any.h:53
This namespace provides templating utilities and introspection into template instantiations.
Definition Templates.h:21
constexpr FType TTypeOf
Definition TypeInfo.h:104
A simplistic but type-safe and RAII compliant storage for anything. Enclosed data is owned by this ty...
Definition Any.h:93
T * TryGet()
Definition Any.h:138
FORCEINLINE FType GetType() const
Definition Any.h:170
FORCEINLINE bool IsValid() const
Definition Any.h:169
decltype(auto) With(this Self &&self, TTypes< T... > &&)
Specify multiple types the enclosed value can be safely cast to, and are valid to be used with TryGet...
Definition Any.h:163
const T * TryGet() const
Definition Any.h:130
FAny(T *newObject, TAnyTypeFacilities< T > const &facilities={})
Definition Any.h:95
FORCEINLINE FAny()
Definition Any.h:124
FAny(FAny &&other)
decltype(auto) WithAlias(this Self &&self)
Specify one type the enclosed value can be safely cast to, and is valid to be used with TryGet.
Definition Any.h:147
FORCEINLINE TSet< FType > const & GetValidTypes() const
Definition Any.h:171
FAny(FAny const &other)
Give the opportunity to customize object lifespan operations for FAny by either specializing this tem...
Definition Any.h:42
TFunction< void(T *)> Destruct
Definition Any.h:43
TFunction< T *(T const &)> CopyConstruct
Definition Any.h:44
This template is used to store pack of types in other templates, or to allow parameter pack inference...
Definition Templates.h:106
Group together type info for identification. Can have an invalid state when no type is specified.
Definition TypeInfo.h:32