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/Limits.h" // MaxValue
6#include "../Foundation/TypeTraits.h" // EnableIf
7#include "ReflectionFoundation.h"
8
9namespace SC
10{
12namespace Reflection
13{
24
27
32{
33 TypeInvalid = 0,
34
35 // Primitive types
36 TypeBOOL = 1,
37 TypeUINT8 = 2,
38 TypeUINT16 = 3,
39 TypeUINT32 = 4,
40 TypeUINT64 = 5,
41 TypeINT8 = 6,
42 TypeINT16 = 7,
43 TypeINT32 = 8,
44 TypeINT64 = 9,
45 TypeFLOAT32 = 10,
46 TypeDOUBLE64 = 11,
47
48 // Non primitive types
49 TypeStruct = 12,
50 TypeArray = 13,
51 TypeVector = 14,
52};
54
63struct TypeInfo
64{
65 bool hasLink : 1;
66 TypeCategory type : 7;
67 union
68 {
69 uint8_t numberOfChildren;
70 uint8_t linkIndex;
71 };
72 uint16_t sizeInBytes;
73
75 struct EmptyInfo
76 {
77 };
78
80 struct MemberInfo
81 {
82 uint16_t memberTag;
83 uint16_t offsetInBytes;
84 constexpr MemberInfo(uint8_t memberTag, uint16_t offsetInBytes)
85 : memberTag(memberTag), offsetInBytes(offsetInBytes)
86 {}
87 };
88
90 struct StructInfo
91 {
92 bool isPacked : 1;
93 constexpr StructInfo(bool isPacked) : isPacked(isPacked) {}
94 };
95
97 struct ArrayInfo
98 {
99 uint32_t isPacked : 1;
100 uint32_t numElements : 31;
101 constexpr ArrayInfo(bool isPacked, uint32_t numElements) : isPacked(isPacked), numElements(numElements) {}
102 };
103 union
104 {
105 EmptyInfo emptyInfo;
106 MemberInfo memberInfo;
107 StructInfo structInfo;
108 ArrayInfo arrayInfo;
109 };
111
113 constexpr TypeInfo()
114 : hasLink(false), type(TypeCategory::TypeInvalid), numberOfChildren(0), sizeInBytes(0), emptyInfo()
115 {
116 static_assert(sizeof(TypeInfo) == 8, "Size must be 8 bytes");
117 }
118
120 constexpr TypeInfo(TypeCategory type, uint16_t sizeInBytes, StructInfo structInfo)
121 : hasLink(false), type(type), numberOfChildren(0), sizeInBytes(sizeInBytes), structInfo(structInfo)
122 {}
123
125 constexpr TypeInfo(TypeCategory type, uint16_t sizeInBytes, MemberInfo member)
126 : hasLink(true), type(type), linkIndex(0), sizeInBytes(sizeInBytes), memberInfo(member)
127 {}
128
130 constexpr TypeInfo(TypeCategory type, uint16_t sizeInBytes, uint8_t numberOfChildren, ArrayInfo arrayInfo)
131 : hasLink(false), type(type), numberOfChildren(numberOfChildren), sizeInBytes(sizeInBytes), arrayInfo(arrayInfo)
132 {}
133
135 constexpr TypeInfo(TypeCategory type, uint16_t sizeInBytes)
136 : hasLink(true), type(type), linkIndex(0), sizeInBytes(sizeInBytes), emptyInfo()
137 {}
138
140 [[nodiscard]] constexpr auto getNumberOfChildren() const { return numberOfChildren; }
141
145 [[nodiscard]] constexpr bool setNumberOfChildren(size_t numChildren)
146 {
147 if (numChildren > static_cast<decltype(numberOfChildren)>(MaxValue()))
148 return false;
149 numberOfChildren = static_cast<decltype(numberOfChildren)>(numChildren);
150 return true;
151 }
152
154 [[nodiscard]] constexpr bool hasValidLinkIndex() const { return hasLink and linkIndex > 0; }
155
157 [[nodiscard]] constexpr bool needsLinking() const { return hasLink and linkIndex == 0; }
158
161 [[nodiscard]] constexpr auto getLinkIndex() const { return linkIndex; }
162
166 [[nodiscard]] constexpr bool setLinkIndex(ssize_t newLinkIndex)
167 {
168 if (newLinkIndex > static_cast<decltype(linkIndex)>(MaxValue()))
169 return false;
170 linkIndex = static_cast<decltype(linkIndex)>(newLinkIndex);
171 return true;
172 }
173
175 [[nodiscard]] constexpr bool isPrimitiveType() const { return isPrimitiveCategory(type); }
176
178 [[nodiscard]] constexpr bool isPrimitiveOrPackedStruct() const
179 {
180 if (isPrimitiveType())
181 return true;
182 return (type == Reflection::TypeCategory::TypeStruct) and structInfo.isPacked;
183 }
184
185 [[nodiscard]] static constexpr bool isPrimitiveCategory(TypeCategory category)
186 {
187 return category >= TypeCategory::TypeBOOL and category <= TypeCategory::TypeDOUBLE64;
188 }
189};
191
194template <typename T>
195struct Reflect;
196
198template <typename T, typename SFINAESelector = void>
199struct ExtendedTypeInfo;
200
201//-----------------------------------------------------------------------------------------------------------
202// Primitive Types Support
203//-----------------------------------------------------------------------------------------------------------
204
206struct ReflectPrimitive
207{
208 template <typename TypeVisitor>
209 [[nodiscard]] static constexpr bool build(TypeVisitor&)
210 {
211 return true;
212 }
213};
214
215// clang-format off
216template <> struct Reflect<char> : public ReflectPrimitive {static constexpr auto getCategory(){return TypeCategory::TypeINT8;}};
217template <> struct Reflect<uint8_t> : public ReflectPrimitive {static constexpr auto getCategory(){return TypeCategory::TypeUINT8;}};
218template <> struct Reflect<uint16_t> : public ReflectPrimitive {static constexpr auto getCategory(){return TypeCategory::TypeUINT16;}};
219template <> struct Reflect<uint32_t> : public ReflectPrimitive {static constexpr auto getCategory(){return TypeCategory::TypeUINT32;}};
220template <> struct Reflect<uint64_t> : public ReflectPrimitive {static constexpr auto getCategory(){return TypeCategory::TypeUINT64;}};
221template <> struct Reflect<int8_t> : public ReflectPrimitive {static constexpr auto getCategory(){return TypeCategory::TypeINT8;}};
222template <> struct Reflect<int16_t> : public ReflectPrimitive {static constexpr auto getCategory(){return TypeCategory::TypeINT16;}};
223template <> struct Reflect<int32_t> : public ReflectPrimitive {static constexpr auto getCategory(){return TypeCategory::TypeINT32;}};
224template <> struct Reflect<int64_t> : public ReflectPrimitive {static constexpr auto getCategory(){return TypeCategory::TypeINT64;}};
225template <> struct Reflect<float> : public ReflectPrimitive {static constexpr auto getCategory(){return TypeCategory::TypeFLOAT32;}};
226template <> struct Reflect<double> : public ReflectPrimitive {static constexpr auto getCategory(){return TypeCategory::TypeDOUBLE64;}};
227template <> struct Reflect<bool> : public ReflectPrimitive {static constexpr auto getCategory(){return TypeCategory::TypeBOOL;}};
228
230template <typename T> struct IsPrimitive { static constexpr bool value = TypeInfo::isPrimitiveCategory(Reflect<T>::getCategory()); };
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} // namespace Reflection
339} // namespace SC
340
341// Handy Macros to avoid some typing when wrapping structs
342
344#define SC_REFLECT_STRUCT_VISIT(StructName) \
345 template <> \
346 struct SC::Reflection::Reflect<StructName> : SC::Reflection::ReflectStruct<StructName> \
347 { \
348 template <typename TypeVisitor> \
349 static constexpr bool visit(TypeVisitor&& builder) \
350 { \
351 SC_COMPILER_WARNING_PUSH_OFFSETOF \
352 return true
353
356#define SC_REFLECT_STRUCT_FIELD(MEMBER_TAG, MEMBER) \
357 and builder(MEMBER_TAG, &T::MEMBER, #MEMBER, SC_COMPILER_OFFSETOF(T, MEMBER))
358
360#define SC_REFLECT_STRUCT_LEAVE() \
361 ; \
362 SC_COMPILER_WARNING_POP \
363 } \
364 } \
365 ;
#define SC_COMPILER_UNUSED(param)
Silence an unused variable or unused parameter warning.
Definition Compiler.h:139
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:32
@ 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.