MCRO
C++23 utilities for Unreal Engine.
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages Concepts
TypeName.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/**
13 * @file
14 * @brief Convert types to string
15 */
16
17#pragma once
18
19#include <string>
20#include "ConstexprXXH3.h"
21
22#include "CoreMinimal.h"
23#include "UnrealCtre.h"
24#include "Mcro/TextMacros.h"
25#include "Mcro/Platform.h"
26
27#define MCRO_EXPLICIT_TYPE_EXTRACTION defined(MCRO_EXPLICIT_PRETTY_FUNCTION) && defined(MCRO_EXPLICIT_TYPE_EXTRACT_REGEX)
28
29// TODO: C++ 20's std::source_location was used instead of this bouquet of macros, but then some platforms don't have <source_location>
30#if MCRO_COMPILER_GCC || MCRO_COMPILER_CLANG
31#define PRETTY_FUNC __PRETTY_FUNCTION__
32#elif MCRO_COMPILER_MSVC
33#define PRETTY_FUNC __FUNCSIG__
34#elif MCRO_EXPLICIT_TYPE_EXTRACTION
35#define PRETTY_FUNC MCRO_EXPLICIT_PRETTY_FUNCTION
36#else
37#error "TTypeName is only compatible with Clang, GCC and MSVC. Please create a PR for your compiler at https://github.com/microdee/mcro/pulls.
38#error "If your compiler is under NDA add the following definitions in your target rules (values are examples):"
39#error "`MCRO_EXPLICIT_PRETTY_FUNCTION=__PRETTY_FUNCTION__`"
40#error "`MCRO_EXPLICIT_TYPE_EXTRACT_REGEX=\"GetCompileTimeTypeName\\\\<((struct|class)\\\\s)?(?<TYPE>.+?)\\\\>\\\\(\"`"
41#endif
42
43// disable this warning to allow us exploit division by zero error in consteval
44#pragma warning(push)
45#pragma warning(disable: 4804) // unsafe use of type 'bool' in operation
46
47/**
48 * @brief Get a string view of the compile time typename
49 *
50 * @note
51 * Using std::(w)string here instead of FString or FStringView for consteval compatibility
52 *
53 * @tparam T target type
54 * @return string view of the name of the type
55 */
56template <typename T>
57consteval std::basic_string_view<TCHAR> GetCompileTimeTypeName()
58{
59 using namespace Mcro::Text;
60 std::basic_string_view<TCHAR> thisFunctionName { TEXT(PRETTY_FUNC) };
61
62#if MCRO_COMPILER_GCC || MCRO_COMPILER_CLANG
63
64 // on Clang thisFunctionName looks like this:
65 // matching: [--------------------------------------------]
66 // | capturing: [-------------]|
67 // std::string_view GetCompileTimeTypeName() [T = MyTemplate<Vec>]
68
69 // on GCC thisFunctionName has a similar enough format so we can use the same pattern:
70 // matching: [-------------------------------------------------]
71 // | capturing: [-------------]|
72 // consteval std::string_view GetCompileTimeTypeName() [with T = MyTemplate<Vec>; std::string_view = std::basic_string_view<char>]
73
74 auto result = ctre::search<TEXT_ R"(\s\[(?:with\s)?T\s=\s(?<TYPE>.+?)[;\]])">(thisFunctionName);
75
76#elif MCRO_COMPILER_MSVC
77
78 // on MSVC thisFunctionName looks like this:
79 // matching: [----------------------------------------------------]
80 // | capturing: [---------------------]|
81 // class std::basic_string_view<char,struct std::char_traits<char> > __cdecl GetCompileTimeTypeName<struct MyTemplate<struct Vec>>(void)
82
83 auto result = ctre::search<TEXT_ R"(GetCompileTimeTypeName<((struct|class)\s)?(?<TYPE>.+?)>\‍()">(thisFunctionName);
84
85#elif MCRO_EXPLICIT_TYPE_EXTRACTION
86 auto result = ctre::search<TEXT(MCRO_EXPLICIT_TYPE_EXTRACT_REGEX)>(thisFunctionName);
87#endif
88
89 std::basic_string_view<TCHAR> output = result.get<TEXT_"TYPE">().to_view();
90 size_t size = output.size();
91 size /= output.size() > 0; // Fail compilation with division-by-zero if we couldn't extract a typename
92
93 return size ? output : std::basic_string_view<TCHAR>();
94}
95
96template <typename T>
97consteval uint64 GetCompileTimeTypeHash()
98{
99 std::string_view thisFunctionName { PRETTY_FUNC };
100 return constexpr_xxh3::XXH3_64bits_const(thisFunctionName);
101}
102
103/** Convert types to string */
105{
106 using namespace Mcro::Text;
107
108 /** @brief Group together type info for identification. Can have an invalid state when no type is specified. */
109 struct FType
110 {
111 template <typename T>
112 struct TTag {};
113
114 template <typename T>
115 constexpr FType(TTag<T>&&)
116 : Name(
117 GetCompileTimeTypeName<std::decay_t<T>>().data(),
118 GetCompileTimeTypeName<std::decay_t<T>>().size()
119 )
120 , Hash(GetCompileTimeTypeHash<std::decay_t<T>>())
121 {}
122
123 constexpr FType() {}
124
125 FStringView Name;
126 uint64 Hash = 0;
127
128 constexpr FStringView ToString() const { return Name; }
129 FString ToStringCopy() const { return FString(Name); }
130
131 constexpr bool IsValid() const { return Hash != 0; }
132 constexpr operator bool() const { return IsValid(); }
133
134 friend constexpr bool operator == (FType const& left, FType const& right) { return left.Hash == right.Hash; }
135 friend constexpr bool operator != (FType const& left, FType const& right) { return left.Hash != right.Hash; }
136
137 friend constexpr uint32 GetTypeHash(FType const& self)
138 {
139 return static_cast<uint32>(self.Hash) ^ static_cast<uint32>(self.Hash >> 32);
140 }
141 };
142
143 template <typename T>
145
146 /**
147 * @brief
148 * Get a friendly string of an input type without using `typeid(T).name()`.
149 *
150 * Actually this method gives more predictable results across compilers than `typeid(T).name()` but still not 100%
151 * the same. Besides TTypeName can deal with incomplete types as well, whereas `typeid(T)` cannot (more on this in
152 * remarks). While it is more consistent across compilers, it is still not 100% consistent. Take templated types
153 * for instance where MSVC compiler will add "class" prefix in front of type arguments where GCC or CLang might not.
154 * Because of subtle differences like this, it is strongly discouraged to assume the exact format of the output of
155 * TTypeName
156 *
157 * Usage:
158 * @code
159 * FStringView myTypeName = TTypeName<FMyType>;
160 * @endcode
161 *
162 * It is useful in templates where a type name should be known in runtime as well. (e.g.: Modular features)
163 *
164 * @remarks
165 * Note on incomplete types: incomplete types on MSVC are localized to current scope. which means
166 * @code
167 * class I;
168 *
169 * class A
170 * {
171 * FStringView GetName() { return TTypeName<I>; }
172 * // Returns "I"
173 * }
174 * @endcode
175 * BUT:
176 * @code
177 * class A
178 * {
179 * FStringView GetName() { return TTypeName<class I>; }
180 * // Returns "A::GetName::I"
181 * }
182 * @endcode
183 * as you can see they are not equal. For sake of simplicity TTypeName only supports incomplete
184 * types when they're declared in global scope. Any other case might yield undesired results
185 * (e.g.: `TTypeName<class I>` of incomplete `I` might not be the same as `TTypeName<I>` somewhere
186 * else with `I` being fully defined)
187 *
188 * @tparam T The type to be named
189 *
190 * @warning
191 * Do not use exact type comparison with serialized data or network communication, as the actual value of the type
192 * is different between compilers. Only use this for runtime data. For such scenarios just use Unreal's own UObjects.
193 */
194 template <typename T>
195 constexpr FStringView TTypeName = FStringView(
196 GetCompileTimeTypeName<std::decay_t<T>>().data(),
197 GetCompileTimeTypeName<std::decay_t<T>>().size()
198 );
199
200 /** @brief Same as `TTypeName` just stored as STL string made during compile time */
201 template <typename T>
202 constexpr std::basic_string_view<TCHAR> TTypeNameStd = GetCompileTimeTypeName<std::decay_t<T>>();
203
204 /** @brief Get a fixed `uint64` hash representation of the given type. Have similar caveats as `TTypeName` */
205 template <typename T>
207
208 /**
209 * @brief
210 * Same as `TTypeName` converted to FName. This is not cached and a new FName is created every time this is called.
211 */
212 template <typename T>
214 {
215 return FName(TTypeName<T>.Len(), TTypeName<T>.GetData());
216 }
217
218 /**
219 * @brief
220 * Same as `TTypeName` converted to FString. This is not cached and a new FString is created every time this is called.
221 */
222 template <typename T>
223 FString TTypeString()
224 {
225 return FString::ConstructFromPtrSize(TTypeName<T>.GetData(), TTypeName<T>.Len());
226 }
227}
228
229#pragma warning(pop)
size_t size(TArray< T, A > const &r)
Definition Range.h:98
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:51
consteval std::basic_string_view< TCHAR > GetCompileTimeTypeName()
Get a string view of the compile time typename.
Definition TypeName.h:57
consteval uint64 GetCompileTimeTypeHash()
Definition TypeName.h:97
Mixed text utilities and type traits.
Definition Enums.h:51
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:213
constexpr FType TTypeOf
Definition TypeName.h:144
constexpr FStringView TTypeName
Get a friendly string of an input type without using typeid(T).name().
Definition TypeName.h:195
constexpr uint64 TTypeHash
Get a fixed uint64 hash representation of the given type. Have similar caveats as TTypeName
Definition TypeName.h:206
FString TTypeString()
Same as TTypeName converted to FString. This is not cached and a new FString is created every time th...
Definition TypeName.h:223
constexpr std::basic_string_view< TCHAR > TTypeNameStd
Same as TTypeName just stored as STL string made during compile time.
Definition TypeName.h:202
consteval uint64_t XXH3_64bits_const(const T *input, size_t len) noexcept
Basic interfaces.
Group together type info for identification. Can have an invalid state when no type is specified.
Definition TypeName.h:110
constexpr FType(TTag< T > &&)
Definition TypeName.h:115
FString ToStringCopy() const
Definition TypeName.h:129
friend constexpr bool operator==(FType const &left, FType const &right)
Definition TypeName.h:134
constexpr FType()
Definition TypeName.h:123
friend constexpr bool operator!=(FType const &left, FType const &right)
Definition TypeName.h:135
constexpr bool IsValid() const
Definition TypeName.h:131
constexpr FStringView ToString() const
Definition TypeName.h:128
friend constexpr uint32 GetTypeHash(FType const &self)
Definition TypeName.h:137