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