MCRO
C++23 utilities for Unreal Engine.
Loading...
Searching...
No Matches
Types.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 <string>
15
16#include "CoreMinimal.h"
17#include "Mcro/TypeName.h"
18#include "Mcro/Templates.h"
19#include "Mcro/TypeInfo.h"
20#include "Mcro/SharedObjects.h"
21
22/** @brief C++ native static reflection utilities, not to be confused with reflection of UObjects */
23namespace Mcro::Types
24{
25 using namespace Mcro::TypeName;
26 using namespace Mcro::TypeInfo;
27 using namespace Mcro::Templates;
28 using namespace Mcro::SharedObjects;
29
30 class IHaveType;
31
32 template <typename T>
33 concept CHasTypeName = CDerivedFrom<T, IHaveType>;
34
35 /**
36 * @brief A barebones base class for types which may store their type-name as a string
37 *
38 * @todo
39 * C++ 26 has promising proposal for static value-based reflection, which can gather metadata from classes
40 * or even emit them. The best summary I found so far is a stack-overflow answer https://stackoverflow.com/a/77477029
41 * Once that's available we can gather base classes in compile time, and do dynamic casting of objects without
42 * the need for intrusive extra syntax, or extra work at construction.
43 * Currently GCC's `__bases` would be perfect for the job, but other popular compilers don't have similar intrinsics.
44 *
45 * @warning
46 * Do not use exact type comparison with serialized data or network communication, as the actual value of the type
47 * is different between compilers. Only use this for runtime data. For such scenarios just use Unreal's own UObjects.
48 */
50 {
51 public:
52 virtual ~IHaveType() = default;
53
54 protected:
55 FName TypeName;
57
58 /** @brief This function needs to be called on top level derived type for runtime reflection to work */
59 template <typename Self>
60 void SetType(this Self&& self)
61 {
62 self.TypeName = TTypeFName<Self>();
63 self.TypeInfo = TTypeOf<Self>;
64 }
65
66 public:
67 template <typename Self>
68 using SelfRef = TSharedRef<std::decay_t<Self>>;
69
70 /** @brief Fluent API for setting tpye for deferred initialization (for example in factory functions) */
71 template <CSharedFromThis Self>
72 SelfRef<Self> WithType(this Self&& self)
73 {
74 self.SetType();
75 return SharedSelf(&self);
76 }
77
78 /** @brief Fluent API for setting tpye for deferred initialization (for example in factory functions) */
79 template <typename Self>
80 requires (!CSharedFromThis<Self>)
81 Self&& WithType(this Self&& self)
82 {
83 self.SetType();
84 return FWD(self);
85 }
86
87 FORCEINLINE FType const& GetType() const { return TypeInfo; }
88 FORCEINLINE FName const& GetTypeFName() const { return TypeName; }
89 FORCEINLINE FString GetTypeString() const { return TypeName.ToString(); }
90
91 /**
92 * @brief
93 * Dynamic casting of this object to a derived top-level type. Casting also works if inheritance is done
94 * through `TInherit` template.
95 *
96 * @tparam Derived
97 * Only return the desired type when the current object is exactly that type, and doesn't have deeper
98 * inheritance. Proper dynamic casting regarding the entire inheritance tree still without RTTI will come once
99 * proposed C++26 value-typed reflection becomes wide-spread available among popular compilers. If top-level
100 * derived type used types in `TInherit`, those are also supported.
101 *
102 * @return Object cast to desired type when that's possible (see `Derived`) or nullptr;
103 */
104 template <typename Derived, CSharedFromThis Self>
105 TSharedPtr<Derived> As(this Self&& self)
106 {
107 if constexpr (CDerivedFrom<Derived, Self>)
108 return StaticCastSharedPtr<Derived>(
109 SharedSelf(AsMutablePtr(&self)).ToSharedPtr()
110 );
111 else
112 {
113 if (self.TypeInfo.template IsCompatibleWith<Derived>())
114 return StaticCastSharedPtr<Derived>(
115 SharedSelf(AsMutablePtr(&self)).ToSharedPtr()
116 );
117 return {};
118 }
119 }
120
121 /**
122 * @brief
123 * Dynamic casting of this object to a derived top-level type. Casting also works if inheritance is done
124 * through `TInherit` template.
125 *
126 * @tparam Derived
127 * Only return the desired type when the current object is exactly that type, and doesn't have deeper
128 * inheritance. Proper dynamic casting regarding the entire inheritance tree still without RTTI will come once
129 * proposed C++26 value-typed reflection becomes wide-spread available among popular compilers. If top-level
130 * derived type used types in `TInherit`, those are also supported.
131 *
132 * @return Object cast to desired type when that's possible (see `Derived`) or nullptr;
133 */
134 template <typename Derived, typename Self>
135 requires (!CSharedFromThis<Self>)
136 Derived* As(this Self&& self)
137 {
138 if constexpr (CDerivedFrom<Derived, Self>)
139 return static_cast<Derived*>(&self);
140 else
141 {
142 if (self.TypeInfo.template IsCompatibleWith<Derived>())
143 return static_cast<Derived*>(&self);
144 return nullptr;
145 }
146 }
147 };
148
149 /** @brief Shorthand for combination of `IHaveType` and `TSharedFromThis` */
151 : public IHaveType
152 , public TSharedFromThis<IHaveTypeShareable>
153 {};
154
155 /** @brief Shorthand for combination of `IHaveType` and `TSharedFromThis` where the base-type can be specified */
156 template <typename T>
158 : public IHaveType
159 , public TSharedFromThis<T>
160 {};
161}
#define FWD(...)
Shorten forwarding expression with this macro so one may not need to specify explicit type.
Definition Macros.h:100
Convert types to string.
Shorthand for combination of IHaveType and TSharedFromThis
Definition Types.h:153
A barebones base class for types which may store their type-name as a string.
Definition Types.h:50
virtual ~IHaveType()=default
FORCEINLINE FString GetTypeString() const
Definition Types.h:89
FORCEINLINE FName const & GetTypeFName() const
Definition Types.h:88
Self && WithType(this Self &&self)
Fluent API for setting tpye for deferred initialization (for example in factory functions)
Definition Types.h:81
FORCEINLINE FType const & GetType() const
Definition Types.h:87
Derived * As(this Self &&self)
Dynamic casting of this object to a derived top-level type. Casting also works if inheritance is done...
Definition Types.h:136
void SetType(this Self &&self)
This function needs to be called on top level derived type for runtime reflection to work.
Definition Types.h:60
SelfRef< Self > WithType(this Self &&self)
Fluent API for setting tpye for deferred initialization (for example in factory functions)
Definition Types.h:72
TSharedRef< std::decay_t< Self > > SelfRef
Definition Types.h:68
TSharedPtr< Derived > As(this Self &&self)
Dynamic casting of this object to a derived top-level type. Casting also works if inheritance is done...
Definition Types.h:105
Shorthand for combination of IHaveType and TSharedFromThis where the base-type can be specified.
Definition Types.h:160
Utilities for TSharedPtr/Ref and related.
auto SharedSelf(const T *self) -> TSharedRef< T const, Mode >
Same as SharedThis(this) in TSharedFromThis.
This namespace provides templating utilities and introspection into template instantiations.
Definition Templates.h:21
constexpr auto AsMutablePtr(T *input)
Tired of typing const_cast<FMyLongUnwieldyTypeName*>(...)? use this instead.
Definition Templates.h:279
constexpr FType TTypeOf
Definition TypeInfo.h:104
FName TTypeFName()
Same as TTypeName converted to FName. This is not cached and a new FName is created every time this i...
Definition TypeName.h:179
C++ native static reflection utilities, not to be confused with reflection of UObjects.
Definition Types.h:24
Group together type info for identification. Can have an invalid state when no type is specified.
Definition TypeInfo.h:32