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 "../Foundation/TypeTraits.h" // EnableIf
6#include "ReflectionFoundation.h"
7
8namespace SC
9{
11namespace Reflection
12{
23
26
31{
32 TypeInvalid = 0,
33
34 // Primitive types
35 TypeBOOL = 1,
36 TypeUINT8 = 2,
37 TypeUINT16 = 3,
38 TypeUINT32 = 4,
39 TypeUINT64 = 5,
40 TypeINT8 = 6,
41 TypeINT16 = 7,
42 TypeINT32 = 8,
43 TypeINT64 = 9,
44 TypeFLOAT32 = 10,
45 TypeDOUBLE64 = 11,
46
47 // Non primitive types
48 TypeStruct = 12,
49 TypeArray = 13,
50 TypeVector = 14,
51};
53
62struct TypeInfo
63{
64 bool hasLink : 1;
65 TypeCategory type : 7;
66 union
67 {
68 uint8_t numberOfChildren;
69 uint8_t linkIndex;
70 };
71 uint16_t sizeInBytes;
72
74 struct EmptyInfo
75 {
76 };
77
79 struct MemberInfo
80 {
81 uint16_t memberTag;
82 uint16_t offsetInBytes;
83 constexpr MemberInfo(uint8_t memberTag, uint16_t offsetInBytes)
84 : memberTag(memberTag), offsetInBytes(offsetInBytes)
85 {}
86 };
87
89 struct StructInfo
90 {
91 bool isPacked : 1;
92 constexpr StructInfo(bool isPacked) : isPacked(isPacked) {}
93 };
94
96 struct ArrayInfo
97 {
98 uint32_t isPacked : 1;
99 uint32_t numElements : 31;
100 constexpr ArrayInfo(bool isPacked, uint32_t numElements) : isPacked(isPacked), numElements(numElements) {}
101 };
102 union
103 {
104 EmptyInfo emptyInfo;
105 MemberInfo memberInfo;
106 StructInfo structInfo;
107 ArrayInfo arrayInfo;
108 };
110
112 constexpr TypeInfo()
113 : hasLink(false), type(TypeCategory::TypeInvalid), numberOfChildren(0), sizeInBytes(0), emptyInfo()
114 {
115 static_assert(sizeof(TypeInfo) == 8, "Size must be 8 bytes");
116 }
117
119 constexpr TypeInfo(TypeCategory type, uint16_t sizeInBytes, StructInfo structInfo)
120 : hasLink(false), type(type), numberOfChildren(0), sizeInBytes(sizeInBytes), structInfo(structInfo)
121 {}
122
124 constexpr TypeInfo(TypeCategory type, uint16_t sizeInBytes, MemberInfo member)
125 : hasLink(true), type(type), linkIndex(0), sizeInBytes(sizeInBytes), memberInfo(member)
126 {}
127
129 constexpr TypeInfo(TypeCategory type, uint16_t sizeInBytes, uint8_t numberOfChildren, ArrayInfo arrayInfo)
130 : hasLink(false), type(type), numberOfChildren(numberOfChildren), sizeInBytes(sizeInBytes), arrayInfo(arrayInfo)
131 {}
132
134 constexpr TypeInfo(TypeCategory type, uint16_t sizeInBytes)
135 : hasLink(true), type(type), linkIndex(0), sizeInBytes(sizeInBytes), emptyInfo()
136 {}
137
139 [[nodiscard]] constexpr auto getNumberOfChildren() const { return numberOfChildren; }
140
144 [[nodiscard]] constexpr bool setNumberOfChildren(size_t numChildren)
145 {
146 if (numChildren > static_cast<decltype(numberOfChildren)>(~0ull)) // MaxValue<uint8_t>
147 return false;
148 numberOfChildren = static_cast<decltype(numberOfChildren)>(numChildren);
149 return true;
150 }
151
153 [[nodiscard]] constexpr bool hasValidLinkIndex() const { return hasLink and linkIndex > 0; }
154
156 [[nodiscard]] constexpr bool needsLinking() const { return hasLink and linkIndex == 0; }
157
160 [[nodiscard]] constexpr auto getLinkIndex() const { return linkIndex; }
161
165 [[nodiscard]] constexpr bool setLinkIndex(ssize_t newLinkIndex)
166 {
167 if (newLinkIndex > static_cast<decltype(linkIndex)>(~0ull))
168 return false;
169 linkIndex = static_cast<decltype(linkIndex)>(newLinkIndex);
170 return true;
171 }
172
174 [[nodiscard]] constexpr bool isPrimitiveType() const { return isPrimitiveCategory(type); }
175
177 [[nodiscard]] constexpr bool isPrimitiveOrPackedStruct() const
178 {
179 if (isPrimitiveType())
180 return true;
181 return (type == Reflection::TypeCategory::TypeStruct) and structInfo.isPacked;
182 }
183
184 [[nodiscard]] static constexpr bool isPrimitiveCategory(TypeCategory category)
185 {
186 return category >= TypeCategory::TypeBOOL and category <= TypeCategory::TypeDOUBLE64;
187 }
188};
190
193template <typename T>
194struct Reflect;
195
197template <typename T, typename SFINAESelector = void>
198struct ExtendedTypeInfo;
199
200//-----------------------------------------------------------------------------------------------------------
201// Primitive Types Support
202//-----------------------------------------------------------------------------------------------------------
203
205struct ReflectPrimitive
206{
207 template <typename TypeVisitor>
208 [[nodiscard]] static constexpr bool build(TypeVisitor&)
209 {
210 return true;
211 }
212};
213
214// clang-format off
215template <> struct Reflect<char> : public ReflectPrimitive {static constexpr auto getCategory(){return TypeCategory::TypeINT8;}};
216template <> struct Reflect<uint8_t> : public ReflectPrimitive {static constexpr auto getCategory(){return TypeCategory::TypeUINT8;}};
217template <> struct Reflect<uint16_t> : public ReflectPrimitive {static constexpr auto getCategory(){return TypeCategory::TypeUINT16;}};
218template <> struct Reflect<uint32_t> : public ReflectPrimitive {static constexpr auto getCategory(){return TypeCategory::TypeUINT32;}};
219template <> struct Reflect<uint64_t> : public ReflectPrimitive {static constexpr auto getCategory(){return TypeCategory::TypeUINT64;}};
220template <> struct Reflect<int8_t> : public ReflectPrimitive {static constexpr auto getCategory(){return TypeCategory::TypeINT8;}};
221template <> struct Reflect<int16_t> : public ReflectPrimitive {static constexpr auto getCategory(){return TypeCategory::TypeINT16;}};
222template <> struct Reflect<int32_t> : public ReflectPrimitive {static constexpr auto getCategory(){return TypeCategory::TypeINT32;}};
223template <> struct Reflect<int64_t> : public ReflectPrimitive {static constexpr auto getCategory(){return TypeCategory::TypeINT64;}};
224template <> struct Reflect<float> : public ReflectPrimitive {static constexpr auto getCategory(){return TypeCategory::TypeFLOAT32;}};
225template <> struct Reflect<double> : public ReflectPrimitive {static constexpr auto getCategory(){return TypeCategory::TypeDOUBLE64;}};
226template <> struct Reflect<bool> : public ReflectPrimitive {static constexpr auto getCategory(){return TypeCategory::TypeBOOL;}};
227
229template <typename T> struct IsPrimitive { static constexpr bool value = TypeInfo::isPrimitiveCategory(Reflect<T>::getCategory()); };
230template <typename T> struct IsStruct { static constexpr bool value = Reflect<T>::getCategory() == TypeCategory::TypeStruct; };
231
232// clang-format on
233
234template <typename T>
235struct ExtendedTypeInfo<T, typename SC::TypeTraits::EnableIf<IsPrimitive<T>::value>::type>
236{
237 // Primitive types are packed
238 static constexpr bool IsPacked = true;
239};
240
241//-----------------------------------------------------------------------------------------------------------
242// Arrays Support
243//-----------------------------------------------------------------------------------------------------------
244template <typename T, size_t N>
245struct Reflect<T[N]>
246{
247 static constexpr TypeCategory getCategory() { return TypeCategory::TypeArray; }
248
249 template <typename TypeVisitor>
250 [[nodiscard]] static constexpr bool build(TypeVisitor& builder)
251 {
252 using Type = typename TypeVisitor::Type;
253
254 // Add array type
255 constexpr bool isPacked = ExtendedTypeInfo<T>::IsPacked;
256 if (not builder.addType(Type::template createArray<T[N]>("Array", 1, TypeInfo::ArrayInfo{isPacked, N})))
257 return false;
258
259 // Add dependent item type
260 if (not builder.addType(Type::template createGeneric<T>()))
261 return false;
262
263 return true;
264 }
265};
266
267template <typename T, int N>
268struct ExtendedTypeInfo<T[N]>
269{
270 // Arrays are packed if T is packed
271 static constexpr bool IsPacked = ExtendedTypeInfo<T>::IsPacked;
272};
273
274//-----------------------------------------------------------------------------------------------------------
275// Structs Support
276//-----------------------------------------------------------------------------------------------------------
277template <typename Type>
278struct ReflectStruct
279{
280 using T = Type;
281
282 [[nodiscard]] static constexpr TypeCategory getCategory() { return TypeCategory::TypeStruct; }
283
284 template <typename TypeVisitor>
285 [[nodiscard]] static constexpr bool build(TypeVisitor& builder)
286 {
287 // Add struct type
288 if (not builder.addType(TypeVisitor::Type::template createStruct<T>()))
289 return false;
290
291 // Add all struct member
292 if (not Reflect<Type>::visit(builder))
293 return false;
294 return true;
295 }
296};
297
299template <typename T>
300struct ExtendedStructTypeInfo
301{
302 size_t memberSizeSum = 0;
303 bool IsPacked = false;
304
305 constexpr ExtendedStructTypeInfo()
306 {
307 // Let's call operator() for each member
308 if (Reflect<T>::visit(*this))
309 {
310 // Structs are packed if all of its members are packed (checked in `operator()`)
311 // and in addition to that, summed size of all members is `==` `sizeof(T)`
312 IsPacked = memberSizeSum == sizeof(T);
313 }
314 }
315
316 template <typename R, int N>
317 constexpr bool operator()(int memberTag, R T::* member, const char (&name)[N], size_t offset)
318 {
319 SC_COMPILER_UNUSED(memberTag);
320 SC_COMPILER_UNUSED(name);
321 SC_COMPILER_UNUSED(member);
322 SC_COMPILER_UNUSED(offset);
323 if (not ExtendedTypeInfo<R>().IsPacked)
324 {
325 return false; // If a given type is not packed, let's stop iterating
326 }
327 memberSizeSum += sizeof(R);
328 return true; // continue iterating
329 }
330};
331
332template <typename T, typename SFINAESelector>
333struct ExtendedTypeInfo
334{
335 // Struct is packed if all of its members are packed and sum of their size equals size of struct.
336 static constexpr bool IsPacked = ExtendedStructTypeInfo<T>().IsPacked;
337};
338
339template <typename MemberVisitor, typename Container, typename ItemType, int N>
340struct VectorArrayVTable
341{
342 static constexpr bool build(MemberVisitor&) { return true; }
343};
344} // namespace Reflection
345} // namespace SC
346
347// Handy Macros to avoid some typing when wrapping structs
348
350#define SC_REFLECT_STRUCT_VISIT(StructName) \
351 template <> \
352 struct SC::Reflection::Reflect<StructName> : SC::Reflection::ReflectStruct<StructName> \
353 { \
354 template <typename TypeVisitor> \
355 static constexpr bool visit(TypeVisitor&& builder) \
356 { \
357 SC_COMPILER_WARNING_PUSH_OFFSETOF \
358 return true
359
362#define SC_REFLECT_STRUCT_FIELD(MEMBER_TAG, MEMBER) \
363 and builder(MEMBER_TAG, &T::MEMBER, #MEMBER, SC_COMPILER_OFFSETOF(T, MEMBER))
364
366#define SC_REFLECT_STRUCT_LEAVE() \
367 ; \
368 SC_COMPILER_WARNING_POP \
369 } \
370 } \
371 ;
#define SC_COMPILER_UNUSED(param)
Silence an unused variable or unused parameter warning.
Definition Compiler.h:131
unsigned short uint16_t
Platform independent (2) bytes unsigned int.
Definition PrimitiveTypes.h:37
unsigned char uint8_t
Platform independent (1) byte unsigned int.
Definition PrimitiveTypes.h:36
unsigned long long uint64_t
Platform independent (8) bytes unsigned int.
Definition PrimitiveTypes.h:42
unsigned int uint32_t
Platform independent (4) bytes unsigned int.
Definition PrimitiveTypes.h:38
short int16_t
Platform independent (2) bytes signed int.
Definition PrimitiveTypes.h:45
long long int64_t
Platform independent (8) bytes signed int.
Definition PrimitiveTypes.h:50
signed char int8_t
Platform independent (1) byte signed int.
Definition PrimitiveTypes.h:44
signed long ssize_t
Platform independent signed size type.
Definition PrimitiveTypes.h:57
int int32_t
Platform independent (4) bytes signed int.
Definition PrimitiveTypes.h:46
TypeCategory
Enumeration of possible category types recognized by Reflection.
Definition Reflection.h:31
@ 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.