Sane C++ Libraries
C++ Platform Abstraction Libraries
Loading...
Searching...
No Matches
ReflectionSchemaCompiler.h
1// Copyright (c) Stefano Cristiano
2// SPDX-License-Identifier: MIT
3#pragma once
4#include "Reflection.h"
5
6namespace SC
7{
8namespace Reflection
9{
12
15template <typename SchemaBuilder>
16struct SchemaCompiler
17{
18 private:
19 using Type = typename SchemaBuilder::Type;
20 using TypeBuildFunction = typename Type::TypeBuildFunction;
21 using VirtualTablesType = decltype(SchemaBuilder::vtables);
22
24 template <uint32_t MAX_TOTAL_TYPES>
25 struct FlatFullResult
26 {
27 ArrayWithSize<Type, MAX_TOTAL_TYPES> types;
28 VirtualTablesType vtables;
29 };
30
32 template <uint32_t NUM_TYPES>
33 struct FlatTrimmedResult
34 {
35 ArrayWithSize<TypeInfo, NUM_TYPES> typeInfos;
36 ArrayWithSize<TypeStringView, NUM_TYPES> typeNames;
37 VirtualTablesType vtables;
38 };
39 template <typename Iterator, typename BinaryPredicate>
40 static constexpr void bubbleSort(Iterator first, Iterator last, BinaryPredicate predicate)
41 {
42 if (first >= last)
43 {
44 return;
45 }
46 bool doSwap = true;
47 while (doSwap)
48 {
49 doSwap = false;
50 Iterator p0 = first;
51 Iterator p1 = first + 1;
52 while (p1 != last)
53 {
54 if (predicate(*p1, *p0))
55 {
56 swap(*p1, *p0);
57 doSwap = true;
58 }
59 ++p0;
60 ++p1;
61 }
62 }
63 }
64 template <uint32_t MAX_TYPES>
65 [[nodiscard]] static constexpr bool appendTypesTo(ArrayWithSize<Type, MAX_TYPES>& types, TypeBuildFunction build,
66 SchemaBuilder& builder)
67 {
68 // Let builder write to a slice of our available space in types array
69 const auto baseLinkID = types.size;
70 builder.currentLinkID = types.size;
71 builder.types = {types.values + types.size, MAX_TYPES - types.size};
72
73 if (build(builder))
74 {
75 // Set number of children for parent type and update types array size
76 const auto numberOfTypes = builder.currentLinkID - baseLinkID;
77 const auto numberOfChildren = numberOfTypes - 1;
78 if (numberOfChildren > static_cast<decltype(TypeInfo::numberOfChildren)>(~0ull))
79 return false;
80 if (not types.values[baseLinkID].typeInfo.setNumberOfChildren(numberOfChildren))
81 return false;
82
83 struct OrderByMemberOffset
84 {
85 constexpr bool operator()(const Type& a, const Type& b) const
86 {
87 return a.typeInfo.memberInfo.offsetInBytes < b.typeInfo.memberInfo.offsetInBytes;
88 }
89 };
90 if (types.values[baseLinkID].typeInfo.type == TypeCategory::TypeStruct and
91 types.values[baseLinkID].typeInfo.structInfo.isPacked)
92 {
93 // This is a little help for Binary Serialization, as packed structs end up serialized as is
94 bubbleSort(types.values + baseLinkID + 1, types.values + baseLinkID + 1 + numberOfChildren,
95 OrderByMemberOffset());
96 }
97 types.size += numberOfTypes;
98 return true;
99 }
100 return false;
101 }
102
103 template <uint32_t MAX_LINK_BUFFER_SIZE, uint32_t MAX_TOTAL_TYPES, typename Func>
104 constexpr static FlatFullResult<MAX_TOTAL_TYPES> compileAllTypesFor(Func func)
105 {
106 // Collect all types
107 FlatFullResult<MAX_TOTAL_TYPES> result;
108
109 SchemaBuilder container(result.types.values, MAX_TOTAL_TYPES);
110
111#if SC_COMPILER_GCC // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=77911
112 ArrayWithSize<TypeStringView, MAX_LINK_BUFFER_SIZE> alreadyVisitedTypes;
113#else
114 ArrayWithSize<TypeBuildFunction, MAX_LINK_BUFFER_SIZE> alreadyVisitedTypes;
115#endif
116 ArrayWithSize<uint32_t, MAX_LINK_BUFFER_SIZE> alreadyVisitedLinkID;
117 if (not appendTypesTo(result.types, func, container))
118 {
119 return {};
120 }
121
122 // Link all collected types
123 uint32_t typeIndex = 1;
124 while (typeIndex < result.types.size)
125 {
126 Type& type = result.types.values[typeIndex];
127 if (not type.typeInfo.isPrimitiveType() and type.typeInfo.needsLinking())
128 {
129 uint32_t outIndex = 0;
130#if SC_COMPILER_GCC // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=77911
131 if (alreadyVisitedTypes.contains(type.typeName, &outIndex))
132#else
133 if (alreadyVisitedTypes.contains(type.typeBuild, &outIndex))
134#endif
135 {
136 if (not type.typeInfo.setLinkIndex(alreadyVisitedLinkID.values[outIndex]))
137 return {};
138 }
139 else
140 {
141 if (not type.typeInfo.setLinkIndex(result.types.size))
142 return {};
143 if (not alreadyVisitedLinkID.push_back(result.types.size))
144 return {};
145#if SC_COMPILER_GCC // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=77911
146 if (not alreadyVisitedTypes.push_back(type.typeName))
147#else
148 if (not alreadyVisitedTypes.push_back(type.typeBuild))
149#endif
150 return {};
151 if (not appendTypesTo(result.types, type.typeBuild, container))
152 return {};
153 }
154 }
155 typeIndex++;
156 }
157 result.vtables = container.vtables;
158 return result;
159 }
160
161 public:
169 template <typename T, uint32_t MAX_LINK_BUFFER_SIZE = 20, uint32_t MAX_TOTAL_TYPES = 100>
170 static constexpr auto compile()
171 {
172 constexpr auto schema =
173 compileAllTypesFor<MAX_LINK_BUFFER_SIZE, MAX_TOTAL_TYPES>(&Reflect<T>::template build<SchemaBuilder>);
174 static_assert(schema.types.size > 0, "Something failed in compileAllTypesFor");
175
176 // Trim the returned FlatTrimmedResult only to the effective number of types
177 FlatTrimmedResult<schema.types.size> result;
178 for (uint32_t i = 0; i < schema.types.size; ++i)
179 {
180 result.typeInfos.values[i] = schema.types.values[i].typeInfo;
181 result.typeNames.values[i] = schema.types.values[i].typeName;
182 }
183 result.typeInfos.size = schema.types.size;
184 result.typeNames.size = schema.types.size;
185 result.vtables = schema.vtables;
186 return result;
187 }
188};
189
192template <typename TypeVisitor>
193struct SchemaType
194{
195 using TypeBuildFunction = bool (*)(TypeVisitor& builder);
196
197 TypeInfo typeInfo;
198 TypeStringView typeName;
199 TypeBuildFunction typeBuild;
200
201 constexpr SchemaType() : typeBuild(nullptr) {}
202 constexpr SchemaType(const TypeInfo typeInfo, TypeStringView typeName, TypeBuildFunction typeBuild)
203 : typeInfo(typeInfo), typeName(typeName), typeBuild(typeBuild)
204 {}
205
207 template <typename T>
208 [[nodiscard]] static constexpr SchemaType createGeneric()
209 {
210 return {TypeInfo(Reflect<T>::getCategory(), sizeof(T)), TypeToString<T>::get(), &Reflect<T>::build};
211 }
212
214 template <typename T>
215 [[nodiscard]] static constexpr SchemaType createStruct(TypeStringView name = TypeToString<T>::get())
216 {
217 TypeInfo::StructInfo structInfo(ExtendedTypeInfo<T>::IsPacked);
218 return {TypeInfo(Reflect<T>::getCategory(), sizeof(T), structInfo), name, &Reflect<T>::build};
219 }
220
222 template <typename R, typename T, int N>
223 [[nodiscard]] static constexpr SchemaType createMember(uint8_t memberTag, R T::*, const char (&name)[N],
224 size_t offset)
225 {
226 const auto info = TypeInfo::MemberInfo(memberTag, static_cast<SC::uint16_t>(offset));
227 return {TypeInfo(Reflect<R>::getCategory(), sizeof(R), info), TypeStringView(name, N - 1), &Reflect<R>::build};
228 }
229
231 template <typename T>
232 [[nodiscard]] static constexpr SchemaType createArray(TypeStringView name, uint8_t numChildren,
233 TypeInfo::ArrayInfo arrayInfo)
234 {
235 return {TypeInfo(Reflect<T>::getCategory(), sizeof(T), numChildren, arrayInfo), name, &Reflect<T>::build};
236 }
237};
238
240template <typename TypeVisitor>
241struct SchemaBuilder
242{
243 using Type = SchemaType<TypeVisitor>;
244
245 uint32_t currentLinkID;
246
247 WritableRange<Type> types;
248
249 constexpr SchemaBuilder(Type* output, const uint32_t capacity) : currentLinkID(0), types(output, capacity) {}
250
251 template <typename R, typename T, int N>
252 [[nodiscard]] constexpr bool operator()(uint8_t memberTag, R T::* field, const char (&name)[N], size_t offset)
253 {
254 currentLinkID++;
255 return types.writeAndAdvance(Type::createMember(memberTag, field, name, offset));
256 }
257
258 [[nodiscard]] constexpr bool addType(Type type)
259 {
260 currentLinkID++;
261 return types.writeAndAdvance(type);
262 }
263};
264
266struct FlatSchemaBuilder : public SchemaBuilder<FlatSchemaBuilder>
267{
268 struct EmptyVTables
269 {
270 };
271 EmptyVTables vtables;
272 constexpr FlatSchemaBuilder(Type* output, const uint32_t capacity) : SchemaBuilder(output, capacity) {}
273};
274
276using Schema = Reflection::SchemaCompiler<FlatSchemaBuilder>;
278
279} // namespace Reflection
280
281} // namespace SC
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 int uint32_t
Platform independent (4) bytes unsigned int.
Definition PrimitiveTypes.h:38
constexpr void swap(T &t1, T &t2)
Swaps the values of two objects.
Definition Compiler.h:270
Reflection::SchemaCompiler< FlatSchemaBuilder > Schema
Default schema not building any virtual table.
Definition ReflectionSchemaCompiler.h:276
@ TypeStruct
Type is a struct type.