Sane C++ Libraries
C++ Platform Abstraction Libraries
Loading...
Searching...
No Matches
Reflection.h
1// Copyright (c) Stefano Cristiano
2// SPDX-License-Identifier: MIT
3#pragma once
4
5#include "../Common/CompilerMacrosExport.h"
6#include "../Common/CompilerOffsetOf.h"
7#include "../Common/PrimitiveDefinitions.h"
8#ifndef SC_EXPORT_LIBRARY_REFLECTION
9#define SC_EXPORT_LIBRARY_REFLECTION 0
10#endif
11#define SC_REFLECTION_EXPORT SC_COMPILER_LIBRARY_EXPORT(SC_EXPORT_LIBRARY_REFLECTION)
12
13#include "../Common/TypeTraits.h" // EnableIf
14#include "ReflectionFoundation.h"
15
16namespace SC
17{
19namespace Reflection
20{
31
34
38enum class TypeCategory : uint8_t
39{
40 TypeInvalid = 0,
41
42 // Primitive types
43 TypeBOOL = 1,
44 TypeUINT8 = 2,
45 TypeUINT16 = 3,
46 TypeUINT32 = 4,
47 TypeUINT64 = 5,
48 TypeINT8 = 6,
49 TypeINT16 = 7,
50 TypeINT32 = 8,
51 TypeINT64 = 9,
52 TypeFLOAT32 = 10,
53 TypeDOUBLE64 = 11,
54
55 // Non primitive types
56 TypeStruct = 12,
57 TypeArray = 13,
58 TypeVector = 14,
59};
61
70struct TypeInfo
71{
72 bool hasLink : 1;
73 TypeCategory type : 7;
74 union
75 {
76 uint8_t numberOfChildren;
77 uint8_t linkIndex;
78 };
79 uint16_t sizeInBytes;
80
82 struct EmptyInfo
83 {
84 };
85
87 struct MemberInfo
88 {
89 uint16_t memberTag;
90 uint16_t offsetInBytes;
91 constexpr MemberInfo(uint8_t memberTag, uint16_t offsetInBytes)
92 : memberTag(memberTag), offsetInBytes(offsetInBytes)
93 {}
94 };
95
97 struct StructInfo
98 {
99 bool isPacked : 1;
100 constexpr StructInfo(bool isPacked) : isPacked(isPacked) {}
101 };
102
104 struct ArrayInfo
105 {
106 uint32_t isPacked : 1;
107 uint32_t numElements : 31;
108 constexpr ArrayInfo(bool isPacked, uint32_t numElements) : isPacked(isPacked), numElements(numElements) {}
109 };
110 union
111 {
112 EmptyInfo emptyInfo;
113 MemberInfo memberInfo;
114 StructInfo structInfo;
115 ArrayInfo arrayInfo;
116 };
118
120 constexpr TypeInfo()
121 : hasLink(false), type(TypeCategory::TypeInvalid), numberOfChildren(0), sizeInBytes(0), emptyInfo()
122 {
123 static_assert(sizeof(TypeInfo) == 8, "Size must be 8 bytes");
124 }
125
127 constexpr TypeInfo(TypeCategory type, uint16_t sizeInBytes, StructInfo structInfo)
128 : hasLink(false), type(type), numberOfChildren(0), sizeInBytes(sizeInBytes), structInfo(structInfo)
129 {}
130
132 constexpr TypeInfo(TypeCategory type, uint16_t sizeInBytes, MemberInfo member)
133 : hasLink(true), type(type), linkIndex(0), sizeInBytes(sizeInBytes), memberInfo(member)
134 {}
135
137 constexpr TypeInfo(TypeCategory type, uint16_t sizeInBytes, uint8_t numberOfChildren, ArrayInfo arrayInfo)
138 : hasLink(false), type(type), numberOfChildren(numberOfChildren), sizeInBytes(sizeInBytes), arrayInfo(arrayInfo)
139 {}
140
142 constexpr TypeInfo(TypeCategory type, uint16_t sizeInBytes)
143 : hasLink(true), type(type), linkIndex(0), sizeInBytes(sizeInBytes), emptyInfo()
144 {}
145
147 [[nodiscard]] constexpr auto getNumberOfChildren() const { return numberOfChildren; }
148
152 [[nodiscard]] constexpr bool setNumberOfChildren(size_t numChildren)
153 {
154 if (numChildren > static_cast<decltype(numberOfChildren)>(~0ull)) // MaxValue<uint8_t>
155 return false;
156 numberOfChildren = static_cast<decltype(numberOfChildren)>(numChildren);
157 return true;
158 }
159
161 [[nodiscard]] constexpr bool hasValidLinkIndex() const { return hasLink and linkIndex > 0; }
162
164 [[nodiscard]] constexpr bool needsLinking() const { return hasLink and linkIndex == 0; }
165
168 [[nodiscard]] constexpr auto getLinkIndex() const { return linkIndex; }
169
173 [[nodiscard]] constexpr bool setLinkIndex(ssize_t newLinkIndex)
174 {
175 if (newLinkIndex > static_cast<decltype(linkIndex)>(~0ull))
176 return false;
177 linkIndex = static_cast<decltype(linkIndex)>(newLinkIndex);
178 return true;
179 }
180
182 [[nodiscard]] constexpr bool isPrimitiveType() const { return isPrimitiveCategory(type); }
183
185 [[nodiscard]] constexpr bool isPrimitiveOrPackedStruct() const
186 {
187 if (isPrimitiveType())
188 return true;
189 return (type == Reflection::TypeCategory::TypeStruct) and structInfo.isPacked;
190 }
191
192 [[nodiscard]] static constexpr bool isPrimitiveCategory(TypeCategory category)
193 {
194 return category >= TypeCategory::TypeBOOL and category <= TypeCategory::TypeDOUBLE64;
195 }
196};
198
201template <typename T>
202struct Reflect;
203
205template <typename T, typename SFINAESelector = void>
206struct ExtendedTypeInfo;
207
208//-----------------------------------------------------------------------------------------------------------
209// Primitive Types Support
210//-----------------------------------------------------------------------------------------------------------
211
213struct ReflectPrimitive
214{
215 template <typename TypeVisitor>
216 [[nodiscard]] static constexpr bool build(TypeVisitor&)
217 {
218 return true;
219 }
220};
221
222// clang-format off
223template <> struct Reflect<char> : public ReflectPrimitive {static constexpr auto getCategory(){return TypeCategory::TypeINT8;}};
224template <> struct Reflect<uint8_t> : public ReflectPrimitive {static constexpr auto getCategory(){return TypeCategory::TypeUINT8;}};
225template <> struct Reflect<uint16_t> : public ReflectPrimitive {static constexpr auto getCategory(){return TypeCategory::TypeUINT16;}};
226template <> struct Reflect<uint32_t> : public ReflectPrimitive {static constexpr auto getCategory(){return TypeCategory::TypeUINT32;}};
227template <> struct Reflect<uint64_t> : public ReflectPrimitive {static constexpr auto getCategory(){return TypeCategory::TypeUINT64;}};
228template <> struct Reflect<int8_t> : public ReflectPrimitive {static constexpr auto getCategory(){return TypeCategory::TypeINT8;}};
229template <> struct Reflect<int16_t> : public ReflectPrimitive {static constexpr auto getCategory(){return TypeCategory::TypeINT16;}};
230template <> struct Reflect<int32_t> : public ReflectPrimitive {static constexpr auto getCategory(){return TypeCategory::TypeINT32;}};
231template <> struct Reflect<int64_t> : public ReflectPrimitive {static constexpr auto getCategory(){return TypeCategory::TypeINT64;}};
232template <> struct Reflect<float> : public ReflectPrimitive {static constexpr auto getCategory(){return TypeCategory::TypeFLOAT32;}};
233template <> struct Reflect<double> : public ReflectPrimitive {static constexpr auto getCategory(){return TypeCategory::TypeDOUBLE64;}};
234template <> struct Reflect<bool> : public ReflectPrimitive {static constexpr auto getCategory(){return TypeCategory::TypeBOOL;}};
235
237template <typename T> struct IsPrimitive { static constexpr bool value = TypeInfo::isPrimitiveCategory(Reflect<T>::getCategory()); };
238template <typename T> struct IsStruct { static constexpr bool value = Reflect<T>::getCategory() == TypeCategory::TypeStruct; };
239
240// clang-format on
241
242template <typename T>
243struct ExtendedTypeInfo<T, typename SC::TypeTraits::EnableIf<IsPrimitive<T>::value>::type>
244{
245 // Primitive types are packed
246 static constexpr bool IsPacked = true;
247};
248
249//-----------------------------------------------------------------------------------------------------------
250// Arrays Support
251//-----------------------------------------------------------------------------------------------------------
252template <typename T, size_t N>
253struct Reflect<T[N]>
254{
255 static constexpr TypeCategory getCategory() { return TypeCategory::TypeArray; }
256
257 template <typename TypeVisitor>
258 [[nodiscard]] static constexpr bool build(TypeVisitor& builder)
259 {
260 using Type = typename TypeVisitor::Type;
261
262 // Add array type
263 constexpr bool isPacked = ExtendedTypeInfo<T>::IsPacked;
264 if (not builder.addType(Type::template createArray<T[N]>("Array", 1, TypeInfo::ArrayInfo{isPacked, N})))
265 return false;
266
267 // Add dependent item type
268 if (not builder.addType(Type::template createGeneric<T>()))
269 return false;
270
271 return true;
272 }
273};
274
275template <typename T, int N>
276struct ExtendedTypeInfo<T[N]>
277{
278 // Arrays are packed if T is packed
279 static constexpr bool IsPacked = ExtendedTypeInfo<T>::IsPacked;
280};
281
282//-----------------------------------------------------------------------------------------------------------
283// Structs Support
284//-----------------------------------------------------------------------------------------------------------
285template <typename Type>
286struct ReflectStruct
287{
288 using T = Type;
289
290 [[nodiscard]] static constexpr TypeCategory getCategory() { return TypeCategory::TypeStruct; }
291
292 template <typename TypeVisitor>
293 [[nodiscard]] static constexpr bool build(TypeVisitor& builder)
294 {
295 // Add struct type
296 if (not builder.addType(TypeVisitor::Type::template createStruct<T>()))
297 return false;
298
299 // Add all struct member
300 if (not Reflect<Type>::visit(builder))
301 return false;
302 return true;
303 }
304};
305
307template <typename T>
308struct ExtendedStructTypeInfo
309{
310 size_t memberSizeSum = 0;
311 bool IsPacked = false;
312
313 constexpr ExtendedStructTypeInfo()
314 {
315 // Let's call operator() for each member
316 if (Reflect<T>::visit(*this))
317 {
318 // Structs are packed if all of its members are packed (checked in `operator()`)
319 // and in addition to that, summed size of all members is `==` `sizeof(T)`
320 IsPacked = memberSizeSum == sizeof(T);
321 }
322 }
323
324 template <typename R, int N>
325 constexpr bool operator()(int memberTag, R T::* member, const char (&name)[N], size_t offset)
326 {
327 (void)(memberTag);
328 (void)(name);
329 (void)(member);
330 (void)(offset);
331 if (not ExtendedTypeInfo<R>().IsPacked)
332 {
333 return false; // If a given type is not packed, let's stop iterating
334 }
335 memberSizeSum += sizeof(R);
336 return true; // continue iterating
337 }
338};
339
340template <typename T, typename SFINAESelector>
341struct ExtendedTypeInfo
342{
343 // Struct is packed if all of its members are packed and sum of their size equals size of struct.
344 static constexpr bool IsPacked = ExtendedStructTypeInfo<T>().IsPacked;
345};
346
347template <typename MemberVisitor, typename Container, typename ItemType, int N>
348struct VectorArrayVTable
349{
350 static constexpr bool build(MemberVisitor&) { return true; }
351};
352} // namespace Reflection
353} // namespace SC
354
355// Handy Macros to avoid some typing when wrapping structs
356
358#define SC_REFLECT_STRUCT_VISIT(StructName) \
359 template <> \
360 struct SC::Reflection::Reflect<StructName> : SC::Reflection::ReflectStruct<StructName> \
361 { \
362 template <typename TypeVisitor> \
363 static constexpr bool visit(TypeVisitor&& builder) \
364 { \
365 SC_COMPILER_WARNING_PUSH_OFFSETOF \
366 return true
367
370#define SC_REFLECT_STRUCT_FIELD(MEMBER_TAG, MEMBER) \
371 and builder(MEMBER_TAG, &T::MEMBER, #MEMBER, SC_COMPILER_OFFSETOF(T, MEMBER))
372
374#define SC_REFLECT_STRUCT_LEAVE() \
375 ; \
376 SC_COMPILER_WARNING_POP_OFFSETOF \
377 } \
378 } \
379 ;
TypeCategory
Enumeration of possible category types recognized by Reflection.
Definition Reflection.h:39
@ TypeUINT32
Type is uint32_t
@ TypeUINT16
Type is uint16_t
@ TypeUINT64
Type is uint64_t
@ TypeArray
Type is an array type.
@ TypeINT16
Type is int16_t
@ TypeINT64
Type is int64_t
@ TypeINT32
Type is int32_t
@ TypeVector
Type is a vector type.
@ TypeUINT8
Type is uint8_t
@ TypeStruct
Type is a struct type.
@ TypeDOUBLE64
Type is double
@ TypeInvalid
Invalid type sentinel.