Helios Engine
A modular ECS based data-oriented C++23 game engine framework
Loading...
Searching...
No Matches
type_info.hpp
Go to the documentation of this file.
1#pragma once
2
4
5#include <algorithm>
6#include <compare>
7#include <cstddef>
8#include <cstdint>
9#include <string_view>
10#include <type_traits>
11
12// C++26 reflection support detection.
13// __cpp_impl_reflection guards the language feature (^^ operator),
14// __cpp_lib_reflection guards the library (<meta> / std::meta::*).
15// Both must be present and the library version must meet the minimum revision.
16#if defined(__cpp_impl_reflection) && defined(__cpp_lib_reflection) && \
17 __cpp_lib_reflection >= 202406L
18#include <meta>
19#define HELIOS_HAS_REFLECTION 1
20#else
21#define HELIOS_HAS_REFLECTION 0
22#endif
23
24namespace helios::utils {
25
26namespace details {
27
28#if HELIOS_HAS_REFLECTION
29
30// C++26 reflection path
31// std::meta::display_string_of / identifier_of return string_views backed by
32// static compiler-managed storage, so no FixedString buffering is needed.
33
34/**
35 * @brief Retrieves the fully qualified type name of `T` via C++26 reflection.
36 * @tparam T The type to retrieve the name for
37 * @return The fully qualified type name
38 */
39template <typename T>
40[[nodiscard]] consteval std::string_view GetFullTypeName() noexcept {
41 return std::meta::display_string_of(^^T);
42}
43
44/**
45 * @brief Retrieves the unqualified type name of `T` via C++26 reflection.
46 * @tparam T The type to retrieve the name for
47 * @return The unqualified type name
48 */
49template <typename T>
50[[nodiscard]] consteval std::string_view GetUnqualifiedTypeName() noexcept {
51 return std::meta::identifier_of(^^T);
52}
53
54#else // fallback: compiler-signature scraping
55
56/**
57 * @brief A fixed-size string that can be constructed at compile time.
58 * @tparam N The size of the string (excluding the null terminator)
59 */
60template <size_t N>
62 explicit consteval FixedString(std::string_view sv) noexcept {
63 std::copy_n(sv.begin(), N, data);
64 data[N] = '\0';
65 }
66
67 consteval operator std::string_view() const noexcept { return {data, N}; }
68
69 char data[N + 1];
70};
71
72/**
73 * @brief Extracts the type name of `T` from the compiler-specific function
74 * signature.
75 * @tparam T The type to extract the name for
76 * @return The extracted type name
77 */
78template <typename T>
79[[nodiscard]] consteval std::string_view ExtractTypeName() noexcept {
80#if defined(__clang__)
81 constexpr auto prefix = std::string_view{"[T = "};
82 constexpr auto suffix = std::string_view{"]"};
83 constexpr auto function = std::string_view{__PRETTY_FUNCTION__};
84#elif defined(__GNUC__)
85 constexpr auto prefix = std::string_view{"with T = "};
86 constexpr auto suffix = std::string_view{"]"};
87 constexpr auto function = std::string_view{__PRETTY_FUNCTION__};
88#elif defined(_MSC_VER)
89 constexpr auto prefix = std::string_view{"ExtractTypeName<"};
90 constexpr auto suffix = std::string_view{">(void)"};
91 constexpr auto function = std::string_view{__FUNCSIG__};
92#else
93#error Unsupported compiler
94#endif
95
96 constexpr auto start = function.find(prefix) + prefix.size();
97 constexpr auto end = function.rfind(suffix);
98
99 static_assert(start < end, "Compiler function signature parsing failed!");
100
101#if defined(__GNUC__) && !defined(__clang__)
102 // GCC may append alias expositions after the type parameter, e.g.:
103 // [with T = int; std::string_view = std::basic_string_view<char>]
104 // We need to stop at the first ';' after 'start' if present.
105 constexpr auto semicolon = function.find(';', start);
106 constexpr auto actual_end =
107 (semicolon != std::string_view::npos && semicolon < end) ? semicolon
108 : end;
109 return function.substr(start, actual_end - start);
110#else
111 return function.substr(start, end - start);
112#endif
113}
114
115/**
116 * @brief Counts the number of qualifiers (namespaces and class scopes) in a
117 * fully qualified type name.
118 * @param full_name The fully qualified type name to analyze
119 * @return The number of qualifiers in the type name
120 */
121[[nodiscard]] consteval size_t CountQualifiers(
122 std::string_view full_name) noexcept {
123 size_t count = 0;
124 for (size_t i = 0; i < full_name.size(); ++i) {
125 if (full_name[i] == ':' && i + 1 < full_name.size() &&
126 full_name[i + 1] == ':') {
127 ++count;
128 ++i;
129 }
130 }
131 return count;
132}
133
134/**
135 * @brief Extracts the unqualified type name from a fully qualified type name.
136 * @param full_name The fully qualified type name to extract from
137 * @return The unqualified type name
138 */
139[[nodiscard]] constexpr std::string_view ExtractUnqualifiedName(
140 std::string_view full_name) noexcept {
141 size_t last_separator = 0;
142
143 for (size_t i = 0; i < full_name.size(); ++i) {
144 if (full_name[i] == ':' && i + 1 < full_name.size() &&
145 full_name[i + 1] == ':') {
146 last_separator = i + 2;
147 ++i;
148 }
149 }
150
151 if (last_separator < full_name.size()) {
152 return full_name.substr(last_separator);
153 }
154
155 return full_name;
156}
157
158template <typename T>
159inline constexpr auto kTypeNameStorage =
161
162/**
163 * @brief Retrieves a null-terminated fully qualified type name for `T`.
164 * @tparam T The type to retrieve the name for
165 * @return Pointer to static null-terminated storage
166 */
167template <typename T>
168[[nodiscard]] consteval const char* GetFullTypeNameCString() noexcept {
169 return kTypeNameStorage<T>.data;
170}
171
172/**
173 * @brief Retrieves the fully qualified type name of `T`.
174 * @tparam T The type to retrieve the name for
175 * @return The fully qualified type name
176 */
177template <typename T>
178[[nodiscard]] constexpr std::string_view GetFullTypeName() noexcept {
179 return ExtractTypeName<T>();
180}
181
182/**
183 * @brief Retrieves the unqualified type name of `T`.
184 * @tparam T The type to retrieve the name for
185 * @return The unqualified type name
186 */
187template <typename T>
188[[nodiscard]] constexpr std::string_view GetUnqualifiedTypeName() noexcept {
190}
191
192#endif // HELIOS_HAS_REFLECTION
193
194/**
195 * @brief Computes a hash value for the type `T` based on its fully qualified
196 * name.
197 * @tparam T The type to compute the hash for
198 * @return The computed hash value
199 */
200template <typename T>
201[[nodiscard]] consteval size_t ComputeTypeHash() noexcept {
203}
204
205/**
206 * @brief Detects whether `T` is a lambda closure type.
207 * @details Prefers C++26 reflection when available
208 * (`std::meta::is_class && !std::meta::has_identifier`). Falls back to
209 * compiler-specific type-name heuristics otherwise.
210 * @tparam T Type to inspect
211 * @return `true` when `T` is a lambda closure type
212 */
213template <typename T>
214[[nodiscard]] consteval bool IsLambdaType() noexcept {
215#if HELIOS_HAS_REFLECTION
216 return std::meta::is_class(^^T) && !std::meta::has_identifier(^^T);
217#elif defined(_MSC_VER)
218 constexpr auto name = GetUnqualifiedTypeName<T>();
219 return name.find("<lambda_") != std::string_view::npos;
220#elif defined(__clang__)
221 constexpr auto name = GetUnqualifiedTypeName<T>();
222 return name.find("(lambda at") != std::string_view::npos;
223#elif defined(__GNUC__)
224 constexpr auto name = GetUnqualifiedTypeName<T>();
225 return name.find("<lambda(") != std::string_view::npos ||
226 name.find("<lambda>") != std::string_view::npos;
227#else
228 return false;
229#endif
230}
231
232/**
233 * @brief Detects whether `T` is a regular functor type (not a lambda).
234 * @details Returns `true` for named structs/classes that are not lambda
235 * closure types.
236 * @tparam T Type to inspect
237 * @return `true` when `T` is an object type that is not a lambda
238 */
239template <typename T>
240[[nodiscard]] consteval bool IsFunctorType() noexcept {
241 return std::is_class_v<T> && !IsLambdaType<T>();
242}
243
244} // namespace details
245
246class TypeId;
247
249public:
250 /// @brief Constructs empty `TypeIndex`
251 constexpr TypeIndex() noexcept = default;
252
253 /**
254 * @brief Constructs a `TypeIndex` from a `TypeId`.
255 * @param type_id The `TypeId` to construct from
256 */
257 explicit constexpr TypeIndex(const TypeId& type_id) noexcept;
258 constexpr TypeIndex(const TypeIndex&) noexcept = default;
259 constexpr TypeIndex(TypeIndex&&) noexcept = default;
260 constexpr ~TypeIndex() noexcept = default;
261
262 /**
263 * @brief Constructs a `TypeIndex` from a type `T`.
264 * @tparam T The type to construct the `TypeIndex` for
265 * @return TypeIndex representing the type `T`
266 */
267 template <typename T>
268 [[nodiscard]] static constexpr TypeIndex From() noexcept {
270 }
271
272 /**
273 * @brief Constructs a `TypeIndex` from an instance of type `T`.
274 * @tparam T The type of the instance
275 * @param instance The instance to construct the `TypeIndex` from (unused)
276 * @return TypeIndex representing the type of the instance
277 */
278 template <typename T>
279 [[nodiscard]] static constexpr TypeIndex From(
280 const T& /*instance*/) noexcept {
282 }
283
284 /**
285 * @brief Constructs a `TypeIndex` from a precomputed hash value.
286 * @param hash Type hash value
287 * @return TypeIndex with the given hash
288 */
289 [[nodiscard]] static constexpr TypeIndex FromHash(size_t hash) noexcept {
290 return TypeIndex(hash);
291 }
292
293 constexpr TypeIndex& operator=(const TypeIndex&) noexcept = default;
294 constexpr TypeIndex& operator=(TypeIndex&&) noexcept = default;
295
296 /**
297 * @brief Checks if this `TypeIndex` is empty (i.e., has a hash value of 0).
298 * @return true if this `TypeIndex` is empty, false otherwise
299 */
300 [[nodiscard]] constexpr bool Empty() const noexcept { return hash_ == 0; }
301
302 /**
303 * @brief Retrieves the hash value associated with this `TypeIndex`.
304 * @return Hash value or 0 if this `TypeIndex` is empty
305 */
306 [[nodiscard]] constexpr size_t Hash() const noexcept { return hash_; }
307
308 explicit constexpr operator size_t() const noexcept { return Hash(); }
309
310 [[nodiscard]] constexpr std::strong_ordering operator<=>(
311 const TypeIndex& other) const noexcept = default;
312 [[nodiscard]] constexpr bool operator==(
313 const TypeIndex& other) const noexcept = default;
314
315private:
316 explicit constexpr TypeIndex(size_t hash) noexcept : hash_(hash) {}
317
318 size_t hash_ = 0;
319};
320
321class TypeId {
322public:
323 /// @brief Constructs empty `TypeId`
324 constexpr TypeId() noexcept = default;
325 constexpr TypeId(const TypeId&) noexcept = default;
326 constexpr TypeId(TypeId&&) noexcept = default;
327 constexpr ~TypeId() noexcept = default;
328
329 /**
330 * @brief Constructs a `TypeId` from a type `T`.
331 * @tparam T The type to construct the `TypeId` for
332 * @return TypeId representing the type `T`
333 */
334 template <typename T>
335 [[nodiscard]] static constexpr TypeId From() noexcept {
337 }
338
339 /**
340 * @brief Constructs a `TypeId` from an instance of type `T`.
341 * @tparam T The type of the instance
342 * @param instance The instance to construct the `TypeId` from (unused)
343 * @return TypeId representing the type of the instance
344 */
345 template <typename T>
346 [[nodiscard]] static constexpr TypeId From(const T& /*instance*/) noexcept {
348 }
349
350 /**
351 * @brief Constructs a `TypeId` from a precomputed hash and optional name.
352 * @param hash Type hash (`TypeIndex::Hash()` for the source type)
353 * @param qualified_name Optional qualified type name
354 * @return TypeId with the given identity
355 */
356 [[nodiscard]] static constexpr TypeId FromExported(
357 uint64_t hash, std::string_view qualified_name = {}) noexcept {
358 return {TypeIndex::FromHash(static_cast<size_t>(hash)), qualified_name};
359 }
360
361 constexpr TypeId& operator=(const TypeId&) noexcept = default;
362 constexpr TypeId& operator=(TypeId&&) noexcept = default;
363
364 /**
365 * @brief Checks if this `TypeId` is empty (i.e., has a hash value of 0).
366 * @return true if this `TypeId` is empty, false otherwise
367 */
368 [[nodiscard]] constexpr bool Empty() const noexcept {
369 return type_index_.Empty();
370 }
371
372 /**
373 * @brief Retrieves the unqualified name of the type represented by this
374 * `TypeId`.
375 * @return Unqualified type name or empty string if this `TypeId` is empty
376 */
377 [[nodiscard]] constexpr std::string_view Name() const noexcept {
378 return details::ExtractUnqualifiedName(full_name_);
379 }
380
381 /**
382 * @brief Retrieves the fully qualified name of the type represented by this
383 * `TypeId`.
384 * @return Fully qualified type name or empty string if this `TypeId` is empty
385 */
386 [[nodiscard]] constexpr std::string_view QualifiedName() const noexcept {
387 return full_name_;
388 }
389
390 /**
391 * @brief Retrieves the type index associated with this `TypeId`.
392 * @return TypeIndex for stored type
393 */
394 [[nodiscard]] constexpr TypeIndex Index() const noexcept {
395 return type_index_;
396 }
397
398 [[nodiscard]] constexpr std::strong_ordering operator<=>(
399 const TypeId& other) const noexcept {
400 return type_index_ <=> other.type_index_;
401 }
402
403 [[nodiscard]] constexpr bool operator==(const TypeId& other) const noexcept {
404 return type_index_ == other.type_index_;
405 }
406
407private:
408 constexpr TypeId(TypeIndex type_index, std::string_view full_name) noexcept
409 : type_index_(type_index), full_name_(full_name) {}
410
411 TypeIndex type_index_;
412 std::string_view full_name_;
413};
414
415constexpr TypeIndex::TypeIndex(const TypeId& type_id) noexcept
416 : TypeIndex(type_id.Index()) {}
417
418/**
419 * @brief Compares a `TypeId` with a `TypeIndex` for equality.
420 * @param lhs The `TypeId` to compare
421 * @param rhs The `TypeIndex` to compare
422 * @return `true` if the `TypeId` and `TypeIndex` refer to the same type,
423 * `false` otherwise
424 */
425constexpr bool operator==(const TypeId& lhs, TypeIndex rhs) noexcept {
426 return lhs.Index() == rhs;
427}
428
429/**
430 * @brief Compares a `TypeId` with a `TypeIndex` for ordering.
431 * @param lhs The `TypeId` to compare
432 * @param rhs The `TypeIndex` to compare
433 * @return `std::strong_ordering` result of the comparison
434 */
435constexpr std::strong_ordering operator<=>(const TypeId& lhs,
436 TypeIndex rhs) noexcept {
437 return lhs.Index() <=> rhs;
438}
439
440/**
441 * @brief Compares a `TypeIndex` with a `TypeId` for equality.
442 * @param lhs The `TypeIndex` to compare
443 * @param rhs The `TypeId` to compare
444 * @return `true` if the `TypeIndex` and `TypeId` refer to the same type,
445 * `false` otherwise
446 */
447constexpr bool operator==(TypeIndex lhs, const TypeId& rhs) noexcept {
448 return lhs == rhs.Index();
449}
450
451/**
452 * @brief Compares a `TypeIndex` with a `TypeId` for ordering.
453 * @param lhs The `TypeIndex` to compare
454 * @param rhs The `TypeId` to compare
455 * @return `std::strong_ordering` result of the comparison
456 */
457constexpr std::strong_ordering operator<=>(TypeIndex lhs,
458 const TypeId& rhs) noexcept {
459 return lhs <=> rhs.Index();
460}
461
462/**
463 * @brief Retrieves the unqualified type name of `T`.
464 * @tparam T The type to retrieve the name for
465 * @return Unqualified type name
466 */
467template <typename T>
468[[nodiscard]] constexpr std::string_view TypeNameOf() noexcept {
470}
471
472/**
473 * @brief Retrieves the unqualified type name of the type of the given instance.
474 * @tparam T The type of the instance
475 * @param instance The instance to retrieve the type name for (unused)
476 * @return Unqualified type name
477 */
478template <typename T>
479[[nodiscard]] constexpr std::string_view TypeNameOf(
480 const T& /*instance*/) noexcept {
482}
483
484/**
485 * @brief Retrieves the fully qualified type name of `T`.
486 * @tparam T The type to retrieve the name for
487 * @return Fully qualified type name
488 */
489template <typename T>
490[[nodiscard]] constexpr std::string_view QualifiedTypeNameOf() noexcept {
492}
493
494/**
495 * @brief Retrieves the fully qualified type name of the type of the given
496 * instance.
497 * @tparam T The type of the instance
498 * @param instance The instance to retrieve the type name for (unused)
499 * @return Fully qualified type name
500 */
501template <typename T>
502[[nodiscard]] constexpr std::string_view QualifiedTypeNameOf(
503 const T& /*instance*/) noexcept {
505}
506
507} // namespace helios::utils
508
509namespace std {
510
511template <>
512struct hash<helios::utils::TypeId> {
513 [[nodiscard]] constexpr size_t operator()(
514 const helios::utils::TypeId& type_id) const noexcept {
515 return type_id.Index().Hash();
516 }
517};
518
519template <>
520struct hash<helios::utils::TypeIndex> {
521 [[nodiscard]] constexpr size_t operator()(
522 helios::utils::TypeIndex type_index) const noexcept {
523 return type_index.Hash();
524 }
525};
526
527} // namespace std
constexpr TypeId & operator=(TypeId &&) noexcept=default
constexpr std::strong_ordering operator<=>(const TypeId &other) const noexcept
static constexpr TypeId From(const T &) noexcept
Constructs a TypeId from an instance of type T.
constexpr TypeId & operator=(const TypeId &) noexcept=default
constexpr std::string_view QualifiedName() const noexcept
Retrieves the fully qualified name of the type represented by this TypeId.
constexpr bool operator==(const TypeId &other) const noexcept
static constexpr TypeId From() noexcept
Constructs a TypeId from a type T.
constexpr bool Empty() const noexcept
Checks if this TypeId is empty (i.e., has a hash value of 0).
static constexpr TypeId FromExported(uint64_t hash, std::string_view qualified_name={}) noexcept
Constructs a TypeId from a precomputed hash and optional name.
constexpr TypeIndex Index() const noexcept
Retrieves the type index associated with this TypeId.
constexpr TypeId() noexcept=default
Constructs empty TypeId.
constexpr std::string_view Name() const noexcept
Retrieves the unqualified name of the type represented by this TypeId.
constexpr TypeIndex() noexcept=default
Constructs empty TypeIndex.
static constexpr TypeIndex From() noexcept
Constructs a TypeIndex from a type T.
constexpr TypeIndex & operator=(TypeIndex &&) noexcept=default
constexpr bool operator==(const TypeIndex &other) const noexcept=default
constexpr std::strong_ordering operator<=>(const TypeIndex &other) const noexcept=default
constexpr size_t Hash() const noexcept
Retrieves the hash value associated with this TypeIndex.
static constexpr TypeIndex From(const T &) noexcept
Constructs a TypeIndex from an instance of type T.
constexpr TypeIndex & operator=(const TypeIndex &) noexcept=default
static constexpr TypeIndex FromHash(size_t hash) noexcept
Constructs a TypeIndex from a precomputed hash value.
constexpr bool Empty() const noexcept
Checks if this TypeIndex is empty (i.e., has a hash value of 0).
consteval const char * GetFullTypeNameCString() noexcept
Retrieves a null-terminated fully qualified type name for T.
consteval size_t ComputeTypeHash() noexcept
Computes a hash value for the type T based on its fully qualified name.
constexpr std::string_view ExtractUnqualifiedName(std::string_view full_name) noexcept
Extracts the unqualified type name from a fully qualified type name.
consteval size_t CountQualifiers(std::string_view full_name) noexcept
Counts the number of qualifiers (namespaces and class scopes) in a fully qualified type name.
consteval bool IsFunctorType() noexcept
Detects whether T is a regular functor type (not a lambda).
constexpr std::string_view GetUnqualifiedTypeName() noexcept
Retrieves the unqualified type name of T.
consteval bool IsLambdaType() noexcept
Detects whether T is a lambda closure type.
constexpr std::string_view GetFullTypeName() noexcept
Retrieves the fully qualified type name of T.
consteval std::string_view ExtractTypeName() noexcept
Extracts the type name of T from the compiler-specific function signature.
Definition type_info.hpp:79
constexpr auto kTypeNameStorage
constexpr std::string_view TypeNameOf() noexcept
Retrieves the unqualified type name of T.
constexpr std::string_view QualifiedTypeNameOf() noexcept
Retrieves the fully qualified type name of T.
constexpr bool operator==(const TypeId &lhs, TypeIndex rhs) noexcept
Compares a TypeId with a TypeIndex for equality.
constexpr HashType Fnv1aHash(std::string_view str, HashType hash=kFnvBasis) noexcept
Computes the FNV-1a hash of a string.
Definition hash.hpp:26
constexpr std::strong_ordering operator<=>(const TypeId &lhs, TypeIndex rhs) noexcept
Compares a TypeId with a TypeIndex for ordering.
STL namespace.
A fixed-size string that can be constructed at compile time.
Definition type_info.hpp:61
consteval FixedString(std::string_view sv) noexcept
Definition type_info.hpp:62
constexpr size_t operator()(const helios::utils::TypeId &type_id) const noexcept
constexpr size_t operator()(helios::utils::TypeIndex type_index) const noexcept