Sane C++ Libraries
C++ Platform Abstraction Libraries
ReflectionSchemaCompiler.h
1// Copyright (c) Stefano Cristiano
2// SPDX-License-Identifier: MIT
3#pragma once
4#include "../Algorithms/AlgorithmBubbleSort.h"
5#include "Reflection.h"
6
7namespace SC
8{
9namespace Reflection
10{
13
16template <typename SchemaBuilder>
18{
19 private:
20 using Type = typename SchemaBuilder::Type;
21 using TypeBuildFunction = typename Type::TypeBuildFunction;
22 using VirtualTablesType = decltype(SchemaBuilder::vtables);
23
25 template <uint32_t MAX_TOTAL_TYPES>
26 struct FlatFullResult
27 {
29 VirtualTablesType vtables;
30 };
31
33 template <uint32_t NUM_TYPES>
34 struct FlatTrimmedResult
35 {
38 VirtualTablesType vtables;
39 };
40
41 template <uint32_t MAX_TYPES>
42 [[nodiscard]] static constexpr bool appendTypesTo(ArrayWithSize<Type, MAX_TYPES>& types, TypeBuildFunction build,
43 SchemaBuilder& builder)
44 {
45 // Let builder write to a slice of our available space in types array
46 const auto baseLinkID = types.size;
47 builder.currentLinkID = types.size;
48 builder.types = {types.values + types.size, MAX_TYPES - types.size};
49
50 if (build(builder))
51 {
52 // Set number of children for parent type and update types array size
53 const auto numberOfTypes = builder.currentLinkID - baseLinkID;
54 const auto numberOfChildren = numberOfTypes - 1;
55 if (numberOfChildren > static_cast<decltype(TypeInfo::numberOfChildren)>(MaxValue()))
56 return false;
57 if (not types.values[baseLinkID].typeInfo.setNumberOfChildren(numberOfChildren))
58 return false;
59
60 struct OrderByMemberOffset
61 {
62 constexpr bool operator()(const Type& a, const Type& b) const
63 {
64 return a.typeInfo.memberInfo.offsetInBytes < b.typeInfo.memberInfo.offsetInBytes;
65 }
66 };
67 if (types.values[baseLinkID].typeInfo.type == TypeCategory::TypeStruct and
68 types.values[baseLinkID].typeInfo.structInfo.isPacked)
69 {
70 // This is a little help for Binary Serialization, as packed structs end up serialized as is
71 Algorithms::bubbleSort(types.values + baseLinkID + 1, types.values + baseLinkID + 1 + numberOfChildren,
72 OrderByMemberOffset());
73 }
74 types.size += numberOfTypes;
75 return true;
76 }
77 return false;
78 }
79
80 template <uint32_t MAX_LINK_BUFFER_SIZE, uint32_t MAX_TOTAL_TYPES, typename Func>
81 constexpr static FlatFullResult<MAX_TOTAL_TYPES> compileAllTypesFor(Func func)
82 {
83 // Collect all types
84 FlatFullResult<MAX_TOTAL_TYPES> result;
85
86 SchemaBuilder container(result.types.values, MAX_TOTAL_TYPES);
87
88#if SC_COMPILER_GCC && __GNUC__ <= 13 // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=77911
90#else
92#endif
94 if (not appendTypesTo(result.types, func, container))
95 {
96 return {};
97 }
98
99 // Link all collected types
100 uint32_t typeIndex = 1;
101 while (typeIndex < result.types.size)
102 {
103 Type& type = result.types.values[typeIndex];
104 if (not type.typeInfo.isPrimitiveType() and type.typeInfo.needsLinking())
105 {
106 uint32_t outIndex = 0;
107#if SC_COMPILER_GCC && __GNUC__ <= 13 // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=77911
108 if (alreadyVisitedTypes.contains(type.typeName, &outIndex))
109#else
110 if (alreadyVisitedTypes.contains(type.typeBuild, &outIndex))
111#endif
112 {
113 if (not type.typeInfo.setLinkIndex(alreadyVisitedLinkID.values[outIndex]))
114 return {};
115 }
116 else
117 {
118 if (not type.typeInfo.setLinkIndex(result.types.size))
119 return {};
120 if (not alreadyVisitedLinkID.push_back(result.types.size))
121 return {};
122#if SC_COMPILER_GCC && __GNUC__ <= 13 // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=77911
123 if (not alreadyVisitedTypes.push_back(type.typeName))
124#else
125 if (not alreadyVisitedTypes.push_back(type.typeBuild))
126#endif
127 return {};
128 if (not appendTypesTo(result.types, type.typeBuild, container))
129 return {};
130 }
131 }
132 typeIndex++;
133 }
134 result.vtables = container.vtables;
135 return result;
136 }
137
138 public:
146 template <typename T, uint32_t MAX_LINK_BUFFER_SIZE = 20, uint32_t MAX_TOTAL_TYPES = 100>
147 static constexpr auto compile()
148 {
149 constexpr auto schema =
150 compileAllTypesFor<MAX_LINK_BUFFER_SIZE, MAX_TOTAL_TYPES>(&Reflect<T>::template build<SchemaBuilder>);
151 static_assert(schema.types.size > 0, "Something failed in compileAllTypesFor");
152
153 // Trim the returned FlatTrimmedResult only to the effective number of types
154 FlatTrimmedResult<schema.types.size> result;
155 for (uint32_t i = 0; i < schema.types.size; ++i)
156 {
157 result.typeInfos.values[i] = schema.types.values[i].typeInfo;
158 result.typeNames.values[i] = schema.types.values[i].typeName;
159 }
160 result.typeInfos.size = schema.types.size;
161 result.typeNames.size = schema.types.size;
162 result.vtables = schema.vtables;
163 return result;
164 }
165};
166
169template <typename TypeVisitor>
171{
172 using TypeBuildFunction = bool (*)(TypeVisitor& builder);
173
174 TypeInfo typeInfo;
175 TypeStringView typeName;
176 TypeBuildFunction typeBuild;
177
178 constexpr SchemaType() : typeBuild(nullptr) {}
179 constexpr SchemaType(const TypeInfo typeInfo, TypeStringView typeName, TypeBuildFunction typeBuild)
180 : typeInfo(typeInfo), typeName(typeName), typeBuild(typeBuild)
181 {}
182
184 template <typename T>
185 [[nodiscard]] static constexpr SchemaType createGeneric()
186 {
188 }
189
191 template <typename T>
192 [[nodiscard]] static constexpr SchemaType createStruct(TypeStringView name = TypeToString<T>::get())
193 {
195 return {TypeInfo(Reflect<T>::getCategory(), sizeof(T), structInfo), name, &Reflect<T>::build};
196 }
197
199 template <typename R, typename T, int N>
200 [[nodiscard]] static constexpr SchemaType createMember(uint8_t memberTag, R T::*, const char (&name)[N],
201 size_t offset)
202 {
203 const auto info = TypeInfo::MemberInfo(memberTag, static_cast<SC::uint16_t>(offset));
204 return {TypeInfo(Reflect<R>::getCategory(), sizeof(R), info), TypeStringView(name, N - 1), &Reflect<R>::build};
205 }
206
208 template <typename T>
209 [[nodiscard]] static constexpr SchemaType createArray(TypeStringView name, uint8_t numChildren,
210 TypeInfo::ArrayInfo arrayInfo)
211 {
212 return {TypeInfo(Reflect<T>::getCategory(), sizeof(T), numChildren, arrayInfo), name, &Reflect<T>::build};
213 }
214};
215
217template <typename TypeVisitor>
219{
221
222 uint32_t currentLinkID;
223
225
226 constexpr SchemaBuilder(Type* output, const uint32_t capacity) : currentLinkID(0), types(output, capacity) {}
227
228 template <typename R, typename T, int N>
229 [[nodiscard]] constexpr bool operator()(uint8_t memberTag, R T::*field, const char (&name)[N], size_t offset)
230 {
231 currentLinkID++;
232 return types.writeAndAdvance(Type::createMember(memberTag, field, name, offset));
233 }
234
235 [[nodiscard]] constexpr bool addType(Type type)
236 {
237 currentLinkID++;
238 return types.writeAndAdvance(type);
239 }
240};
241
243struct FlatSchemaBuilder : public SchemaBuilder<FlatSchemaBuilder>
244{
246 {
247 };
248 EmptyVTables vtables;
249 constexpr FlatSchemaBuilder(Type* output, const uint32_t capacity) : SchemaBuilder(output, capacity) {}
250};
251
255
256} // namespace Reflection
257
258} // namespace SC
constexpr void bubbleSort(Iterator first, Iterator last, BinaryPredicate predicate=BinaryPredicate())
Sorts iterator range according to BinaryPredicate (bubble sort).
Definition: AlgorithmBubbleSort.h:34
unsigned char uint8_t
Platform independent (1) byte unsigned int.
Definition: PrimitiveTypes.h:36
unsigned int uint32_t
Platform independent (4) bytes unsigned int.
Definition: PrimitiveTypes.h:38
unsigned short uint16_t
Platform independent (2) bytes unsigned int.
Definition: PrimitiveTypes.h:37
@ TypeStruct
Type is a struct type.
An object that can be converted to any primitive type providing its max value.
Definition: Limits.h:21
A constexpr array.
Definition: ReflectionFoundation.h:18
constexpr bool push_back(const T &value)
Append a single item to this array.
Definition: ReflectionFoundation.h:60
constexpr bool contains(T value, uint32_t *outIndex=nullptr) const
Check if array contains given value, and retrieve index where such item exists.
Definition: ReflectionFoundation.h:27
Class template used to check if a given type IsPacked property is true at compile time.
Definition: Reflection.h:334
Definition: ReflectionSchemaCompiler.h:246
A schema builder that doesn't build any virtual table.
Definition: ReflectionSchemaCompiler.h:244
Basic class template that must be partially specialized for each type.
Definition: Reflection.h:195
Common code for derived class to create a SchemaBuilder suitable for SC::Reflection::SchemaCompiler.
Definition: ReflectionSchemaCompiler.h:219
Creates a schema linking a series of SchemaType.
Definition: ReflectionSchemaCompiler.h:18
static constexpr auto compile()
Returns a constexpr compiled trimmed flat schema for type T.
Definition: ReflectionSchemaCompiler.h:147
Holds together a TypeInfo, a StringView and a type-erased builder function pointer.
Definition: ReflectionSchemaCompiler.h:171
static constexpr SchemaType createArray(TypeStringView name, uint8_t numChildren, TypeInfo::ArrayInfo arrayInfo)
Create from an array-like type.
Definition: ReflectionSchemaCompiler.h:209
static constexpr SchemaType createMember(uint8_t memberTag, R T::*, const char(&name)[N], size_t offset)
Create from a struct member with given name, memberTag and offset.
Definition: ReflectionSchemaCompiler.h:200
static constexpr SchemaType createGeneric()
Create from a generic type T.
Definition: ReflectionSchemaCompiler.h:185
static constexpr SchemaType createStruct(TypeStringView name=TypeToString< T >::get())
Create from a Struct type T.
Definition: ReflectionSchemaCompiler.h:192
A minimal ASCII StringView with shortened name to be used in TypeToString.
Definition: ReflectionFoundation.h:101
Holds extended type info for array-like types.
Definition: Reflection.h:98
Holds extended type info for members of struct.
Definition: Reflection.h:81
Holds extended type info for structs.
Definition: Reflection.h:91
[reflectionSnippet3]
Definition: Reflection.h:64
uint8_t numberOfChildren
Only valid when TypeInfo::hasLink == false
Definition: Reflection.h:69
Strips down class name produced by ClNm to reduce binary size (from C++17 going forward)
Definition: ReflectionFoundation.h:178
A writable span of objects.
Definition: ReflectionFoundation.h:79