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()); };
230
231// clang-format on
232
233template <typename T>
234struct ExtendedTypeInfo<T, typename SC::TypeTraits::EnableIf<IsPrimitive<T>::value>::type>
235{
236 // Primitive types are packed
237 static constexpr bool IsPacked = true;
238};
239
240//-----------------------------------------------------------------------------------------------------------
241// Arrays Support
242//-----------------------------------------------------------------------------------------------------------
243template <typename T, size_t N>
244struct Reflect<T[N]>
245{
246 static constexpr TypeCategory getCategory() { return TypeCategory::TypeArray; }
247
248 template <typename TypeVisitor>
249 [[nodiscard]] static constexpr bool build(TypeVisitor& builder)
250 {
251 using Type = typename TypeVisitor::Type;
252
253 // Add array type
254 constexpr bool isPacked = ExtendedTypeInfo<T>::IsPacked;
255 if (not builder.addType(Type::template createArray<T[N]>("Array", 1, TypeInfo::ArrayInfo{isPacked, N})))
256 return false;
257
258 // Add dependent item type
259 if (not builder.addType(Type::template createGeneric<T>()))
260 return false;
261
262 return true;
263 }
264};
265
266template <typename T, int N>
267struct ExtendedTypeInfo<T[N]>
268{
269 // Arrays are packed if T is packed
270 static constexpr bool IsPacked = ExtendedTypeInfo<T>::IsPacked;
271};
272
273//-----------------------------------------------------------------------------------------------------------
274// Structs Support
275//-----------------------------------------------------------------------------------------------------------
276template <typename Type>
277struct ReflectStruct
278{
279 using T = Type;
280
281 [[nodiscard]] static constexpr TypeCategory getCategory() { return TypeCategory::TypeStruct; }
282
283 template <typename TypeVisitor>
284 [[nodiscard]] static constexpr bool build(TypeVisitor& builder)
285 {
286 // Add struct type
287 if (not builder.addType(TypeVisitor::Type::template createStruct<T>()))
288 return false;
289
290 // Add all struct member
291 if (not Reflect<Type>::visit(builder))
292 return false;
293 return true;
294 }
295};
296
298template <typename T>
299struct ExtendedStructTypeInfo
300{
301 size_t memberSizeSum = 0;
302 bool IsPacked = false;
303
304 constexpr ExtendedStructTypeInfo()
305 {
306 // Let's call operator() for each member
307 if (Reflect<T>::visit(*this))
308 {
309 // Structs are packed if all of its members are packed (checked in `operator()`)
310 // and in addition to that, summed size of all members is `==` `sizeof(T)`
311 IsPacked = memberSizeSum == sizeof(T);
312 }
313 }
314
315 template <typename R, int N>
316 constexpr bool operator()(int memberTag, R T::* member, const char (&name)[N], size_t offset)
317 {
318 SC_COMPILER_UNUSED(memberTag);
319 SC_COMPILER_UNUSED(name);
320 SC_COMPILER_UNUSED(member);
321 SC_COMPILER_UNUSED(offset);
322 if (not ExtendedTypeInfo<R>().IsPacked)
323 {
324 return false; // If a given type is not packed, let's stop iterating
325 }
326 memberSizeSum += sizeof(R);
327 return true; // continue iterating
328 }
329};
330
331template <typename T, typename SFINAESelector>
332struct ExtendedTypeInfo
333{
334 // Struct is packed if all of its members are packed and sum of their size equals size of struct.
335 static constexpr bool IsPacked = ExtendedStructTypeInfo<T>().IsPacked;
336};
337} // namespace Reflection
338} // namespace SC
339
340// Handy Macros to avoid some typing when wrapping structs
341
343#define SC_REFLECT_STRUCT_VISIT(StructName) \
344 template <> \
345 struct SC::Reflection::Reflect<StructName> : SC::Reflection::ReflectStruct<StructName> \
346 { \
347 template <typename TypeVisitor> \
348 static constexpr bool visit(TypeVisitor&& builder) \
349 { \
350 SC_COMPILER_WARNING_PUSH_OFFSETOF \
351 return true
352
355#define SC_REFLECT_STRUCT_FIELD(MEMBER_TAG, MEMBER) \
356 and builder(MEMBER_TAG, &T::MEMBER, #MEMBER, SC_COMPILER_OFFSETOF(T, MEMBER))
357
359#define SC_REFLECT_STRUCT_LEAVE() \
360 ; \
361 SC_COMPILER_WARNING_POP \
362 } \
363 } \
364 ;
#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.