Sane C++ Libraries
C++ Platform Abstraction Libraries
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/TypeList.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
64{
65 bool hasLink : 1;
67 union
68 {
71 };
73
75 struct EmptyInfo
76 {
77 };
78
81 {
86 {}
87 };
88
91 {
92 bool isPacked : 1;
93 constexpr StructInfo(bool isPacked) : isPacked(isPacked) {}
94 };
95
97 struct ArrayInfo
98 {
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
121 : hasLink(false), type(type), numberOfChildren(0), sizeInBytes(sizeInBytes), structInfo(structInfo)
122 {}
123
126 : hasLink(true), type(type), linkIndex(0), sizeInBytes(sizeInBytes), memberInfo(member)
127 {}
128
131 : hasLink(false), type(type), numberOfChildren(numberOfChildren), sizeInBytes(sizeInBytes), arrayInfo(arrayInfo)
132 {}
133
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
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>
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>
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>
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
int int32_t
Platform independent (4) bytes signed int.
Definition: PrimitiveTypes.h:46
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
signed char int8_t
Platform independent (1) byte signed int.
Definition: PrimitiveTypes.h:44
long long int64_t
Platform independent (8) bytes signed int.
Definition: PrimitiveTypes.h:50
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
unsigned short uint16_t
Platform independent (2) bytes unsigned int.
Definition: PrimitiveTypes.h:37
signed long ssize_t
Platform independent signed size type.
Definition: PrimitiveTypes.h:57
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.
An object that can be converted to any primitive type providing its max value.
Definition: Limits.h:21
Visit all struct members to gather sum of their sizes (helper for IsPacked).
Definition: Reflection.h:301
Class template used to check if a given type IsPacked property is true at compile time.
Definition: Reflection.h:334
Checks if a given type is primitive.
Definition: Reflection.h:230
Basic class template that must be partially specialized for each type.
Definition: Reflection.h:195
Base struct for all primitive types.
Definition: Reflection.h:207
Definition: Reflection.h:279
Holds extended type info for array-like types.
Definition: Reflection.h:98
uint32_t isPacked
Ensures no padding (recursively) for the entire span of the struct.
Definition: Reflection.h:99
uint32_t numElements
Number of elements in the array.
Definition: Reflection.h:100
Holds no extended type info.
Definition: Reflection.h:76
Holds extended type info for members of struct.
Definition: Reflection.h:81
uint16_t offsetInBytes
Used for signature uniqueness and by SerializationBinaryTypeErased.
Definition: Reflection.h:83
uint16_t memberTag
Used for versioned serialization.
Definition: Reflection.h:82
Holds extended type info for structs.
Definition: Reflection.h:91
bool isPacked
Ensures no padding (recursively) for the entire span of the struct.
Definition: Reflection.h:92
[reflectionSnippet3]
Definition: Reflection.h:64
uint8_t numberOfChildren
Only valid when TypeInfo::hasLink == false
Definition: Reflection.h:69
constexpr bool needsLinking() const
Check if this type info needs to be linked.
Definition: Reflection.h:157
constexpr auto getLinkIndex() const
Obtains link valid index (assuming hasLink == true and needsLinking() == false)
Definition: Reflection.h:161
constexpr bool hasValidLinkIndex() const
Check if this type info has a valid link index.
Definition: Reflection.h:154
constexpr TypeInfo()
[reflectionSnippet4]
Definition: Reflection.h:113
constexpr TypeInfo(TypeCategory type, uint16_t sizeInBytes)
Constructs a TypeInfo of given type and size.
Definition: Reflection.h:135
constexpr bool isPrimitiveOrPackedStruct() const
Check if type is primitive or it's a struct with isPacked property == true
Definition: Reflection.h:178
TypeCategory type
Type of typeinfo.
Definition: Reflection.h:66
constexpr TypeInfo(TypeCategory type, uint16_t sizeInBytes, MemberInfo member)
Constructs a TypeInfo used by Struct Members (children of Struct Type)
Definition: Reflection.h:125
bool hasLink
Contains a link to another type.
Definition: Reflection.h:65
constexpr bool setLinkIndex(ssize_t newLinkIndex)
Change Link index for this type.
Definition: Reflection.h:166
constexpr TypeInfo(TypeCategory type, uint16_t sizeInBytes, StructInfo structInfo)
Constructs a TypeInfo used by Struct Types.
Definition: Reflection.h:120
uint8_t linkIndex
Only valid when TypeInfo::hasLink == true
Definition: Reflection.h:70
uint16_t sizeInBytes
Size in bytes of the described type.
Definition: Reflection.h:72
constexpr bool setNumberOfChildren(size_t numChildren)
Sets the number of children of this typeinfo.
Definition: Reflection.h:145
constexpr TypeInfo(TypeCategory type, uint16_t sizeInBytes, uint8_t numberOfChildren, ArrayInfo arrayInfo)
Constructs a TypeInfo used by Array-like Types (T[N], Array<T, N> and Vector<T>)
Definition: Reflection.h:130
constexpr auto getNumberOfChildren() const
Get number of children (if any) of this info. Only valid when hasLink == false
Definition: Reflection.h:140
constexpr bool isPrimitiveType() const
Check if type is primitive.
Definition: Reflection.h:175
EnableIf conditionally defines a type if a boolean template parameter is true.
Definition: TypeTraits.h:25