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