Reflection for RapidJSON 0.0.16
Reflection for serializing/deserializing with RapidJSON
Loading...
Searching...
No Matches
reflector.h
Go to the documentation of this file.
1#ifndef REFLECTIVE_RAPIDJSON_BINARY_REFLECTOR_H
2#define REFLECTIVE_RAPIDJSON_BINARY_REFLECTOR_H
3
10#include "../traits.h"
11#include "../versioning.h"
12
13#include <c++utilities/conversion/conversionexception.h>
14#include <c++utilities/io/binaryreader.h>
15#include <c++utilities/io/binarywriter.h>
16
17#include <any>
18#include <limits>
19#include <memory>
20#include <optional>
21#include <string>
22#include <variant>
23
27
28namespace ReflectiveRapidJSON {
29
33template <typename T> struct AdaptedBinarySerializable : public Traits::Bool<false> {
34 static constexpr const char *name = "AdaptedBinarySerializable";
35 static constexpr const char *qualifiedName = "ReflectiveRapidJSON::AdaptedBinarySerializable";
36};
37
38using BinaryVersion = std::uint64_t;
39template <typename Type, BinaryVersion v = 0> struct BinarySerializable;
40
44namespace BinaryReflector {
45
46// define traits to distinguish between "built-in" types like int, std::string, std::vector, ... and custom structs/classes
47template <typename Type>
48using IsBuiltInType = Traits::Any<Traits::IsAnyOf<Type, char, std::uint8_t, bool, std::string, std::int16_t, std::uint16_t, std::int32_t,
49 std::uint32_t, std::int64_t, std::uint64_t, float, double>,
50 Traits::IsIteratable<Type>, Traits::IsSpecializingAnyOf<Type, std::pair, std::unique_ptr, std::shared_ptr, std::optional>, std::is_enum<Type>,
52template <typename Type> using IsCustomType = Traits::Not<IsBuiltInType<Type>>;
53
56
62template <typename Type, Traits::EnableIf<IsCustomType<Type>> * = nullptr>
63BinaryVersion readCustomType(BinaryDeserializer &deserializer, Type &customType, BinaryVersion version = 0);
64
69template <typename Type, Traits::EnableIf<IsCustomType<Type>> * = nullptr>
70void writeCustomType(BinarySerializer &serializer, const Type &customType, BinaryVersion version = 0);
71
73class BinaryDeserializer : public CppUtilities::BinaryReader {
74 friend class ::BinaryReflectorTests;
75
76public:
77 explicit BinaryDeserializer(std::istream *stream);
78
79 using CppUtilities::BinaryReader::read;
80 template <typename Type, Traits::EnableIf<Traits::IsSpecializationOf<Type, std::pair>> * = nullptr> void read(Type &pair);
81 template <typename Type, Traits::EnableIf<Traits::IsSpecializationOf<Type, std::unique_ptr>> * = nullptr> void read(Type &pointer);
82 template <typename Type, Traits::EnableIf<Traits::IsSpecializationOf<Type, std::shared_ptr>> * = nullptr> void read(Type &pointer);
83 template <typename Type, Traits::EnableIf<Traits::IsSpecializationOf<Type, std::optional>> * = nullptr> void read(Type &pointer);
84 template <typename Type, Traits::EnableIf<IsArray<Type>, Traits::IsResizable<Type>> * = nullptr> void read(Type &iteratable);
85 template <typename Type, Traits::EnableIfAny<IsMapOrHash<Type>, IsMultiMapOrHash<Type>> * = nullptr> void read(Type &iteratable);
86 template <typename Type,
87 Traits::EnableIf<IsIteratableExceptString<Type>,
88 Traits::None<IsMapOrHash<Type>, IsMultiMapOrHash<Type>, Traits::All<IsArray<Type>, Traits::IsResizable<Type>>>> * = nullptr>
89 void read(Type &iteratable);
90 template <typename Type, Traits::EnableIf<std::is_enum<Type>> * = nullptr> void read(Type &enumValue);
91 template <typename Type, Traits::EnableIf<IsVariant<Type>> * = nullptr> void read(Type &variant);
92 template <typename Type, Traits::EnableIf<IsBuiltInType<Type>> * = nullptr> BinaryVersion read(Type &builtInType, BinaryVersion version);
93 template <typename Type, Traits::EnableIf<IsCustomType<Type>> * = nullptr> BinaryVersion read(Type &customType, BinaryVersion version = 0);
94
95private:
96 std::unordered_map<std::uint64_t, std::any> m_pointer;
97};
98
100class BinarySerializer : public CppUtilities::BinaryWriter {
101 friend class ::BinaryReflectorTests;
102
103public:
104 explicit BinarySerializer(std::ostream *stream);
105
106 using CppUtilities::BinaryWriter::write;
107 template <typename Type, Traits::EnableIf<Traits::IsSpecializationOf<Type, std::pair>> * = nullptr> void write(const Type &pair);
108 template <typename Type, Traits::EnableIf<Traits::IsSpecializingAnyOf<Type, std::unique_ptr, std::optional>> * = nullptr>
109 void write(const Type &pointer);
110 template <typename Type, Traits::EnableIf<Traits::IsSpecializingAnyOf<Type, std::shared_ptr>> * = nullptr> void write(const Type &pointer);
111 template <typename Type, Traits::EnableIf<IsIteratableExceptString<Type>, Traits::HasSize<Type>> * = nullptr> void write(const Type &iteratable);
112 template <typename Type, Traits::EnableIf<std::is_enum<Type>> * = nullptr> void write(const Type &enumValue);
113 template <typename Type, Traits::EnableIf<IsVariant<Type>> * = nullptr> void write(const Type &variant);
114 template <typename Type, Traits::EnableIf<IsBuiltInType<Type>> * = nullptr> void write(const Type &builtInType, BinaryVersion version);
115 template <typename Type, Traits::EnableIf<IsCustomType<Type>> * = nullptr> void write(const Type &customType, BinaryVersion version = 0);
116
117private:
118 std::unordered_map<std::uint64_t, bool> m_pointer;
119};
120
121inline BinaryDeserializer::BinaryDeserializer(std::istream *stream)
122 : CppUtilities::BinaryReader(stream)
123{
124}
125
126template <typename Type, Traits::EnableIf<Traits::IsSpecializationOf<Type, std::pair>> *> void BinaryDeserializer::read(Type &pair)
127{
128 read(pair.first);
129 read(pair.second);
130}
131
132template <typename Type, Traits::EnableIf<Traits::IsSpecializationOf<Type, std::unique_ptr>> *> void BinaryDeserializer::read(Type &pointer)
133{
134 if (!readBool()) {
135 pointer.reset();
136 return;
137 }
138 pointer = std::make_unique<typename Type::element_type>();
139 read(*pointer);
140}
141
142template <typename Type, Traits::EnableIf<Traits::IsSpecializationOf<Type, std::shared_ptr>> *> void BinaryDeserializer::read(Type &pointer)
143{
144 auto mode = readByte();
145 if (!mode) {
146 // pointer not set
147 pointer.reset();
148 return;
149 }
150
151 const auto id = (mode & 0x4) ? readUInt64BE() : readVariableLengthUIntBE(); // the 3rd bit being flagged indicates a big ID
152 if ((mode & 0x3) == 1) {
153 // first occurrence: make a new pointer
154 m_pointer[id] = pointer = std::make_shared<typename Type::element_type>();
155 read(*pointer);
156 return;
157 }
158 // further occurrences: copy previous pointer
159 try {
160 pointer = std::any_cast<Type>(m_pointer[id]);
161 } catch (const std::bad_any_cast &) {
162 throw CppUtilities::ConversionException("Referenced pointer type does not match");
163 }
164}
165
166template <typename Type, Traits::EnableIf<Traits::IsSpecializationOf<Type, std::optional>> *> void BinaryDeserializer::read(Type &opt)
167{
168 if (readBool()) {
169 read(opt.emplace());
170 } else {
171 opt.reset();
172 }
173}
174
175template <typename Type, Traits::EnableIf<IsArray<Type>, Traits::IsResizable<Type>> *> void BinaryDeserializer::read(Type &iteratable)
176{
177 const auto size = readVariableLengthUIntBE();
178 iteratable.resize(size);
179 for (auto &element : iteratable) {
180 read(element);
181 }
182}
183
184template <typename Type, Traits::EnableIfAny<IsMapOrHash<Type>, IsMultiMapOrHash<Type>> *> void BinaryDeserializer::read(Type &iteratable)
185{
186 const auto size = readVariableLengthUIntBE();
187 for (size_t i = 0; i != size; ++i) {
188 std::pair<typename std::remove_const<typename Type::value_type::first_type>::type, typename Type::value_type::second_type> value;
189 read(value);
190 iteratable.emplace(std::move(value));
191 }
192}
193
194template <typename Type,
195 Traits::EnableIf<IsIteratableExceptString<Type>,
196 Traits::None<IsMapOrHash<Type>, IsMultiMapOrHash<Type>, Traits::All<IsArray<Type>, Traits::IsResizable<Type>>>> *>
197void BinaryDeserializer::read(Type &iteratable)
198{
199 const auto size = readVariableLengthUIntBE();
200 for (size_t i = 0; i != size; ++i) {
201 typename Type::value_type value;
202 read(value);
203 iteratable.emplace(std::move(value));
204 }
205}
206
207template <typename Type, Traits::EnableIf<std::is_enum<Type>> *> void BinaryDeserializer::read(Type &enumValue)
208{
209 typename std::underlying_type<Type>::type value;
210 read(value);
211 enumValue = static_cast<Type>(value);
212}
213
215namespace Detail {
216template <typename Variant, std::size_t compiletimeIndex = 0>
217void readVariantValueByRuntimeIndex(std::size_t runtimeIndex, Variant &variant, BinaryDeserializer &deserializer)
218{
219 if constexpr (compiletimeIndex < std::variant_size_v<Variant>) {
220 if (compiletimeIndex == runtimeIndex) {
221 if constexpr (std::is_same_v<std::variant_alternative_t<compiletimeIndex, Variant>, std::monostate>) {
222 variant = std::monostate{};
223 } else {
224 deserializer.read(variant.template emplace<compiletimeIndex>());
225 }
226 } else {
227 readVariantValueByRuntimeIndex<Variant, compiletimeIndex + 1>(runtimeIndex, variant, deserializer);
228 }
229 } else {
230 throw CppUtilities::ConversionException("Variant index is out of expected range");
231 }
232}
233} // namespace Detail
235
236template <typename Type, Traits::EnableIf<IsVariant<Type>> *> void BinaryDeserializer::read(Type &variant)
237{
238 Detail::readVariantValueByRuntimeIndex(readByte(), variant, *this);
239}
240
241template <typename Type, Traits::EnableIf<IsBuiltInType<Type>> *> BinaryVersion BinaryDeserializer::read(Type &builtInType, BinaryVersion version)
242{
243 read(builtInType);
244 return version;
245}
246
247template <typename Type, Traits::EnableIf<IsCustomType<Type>> *> BinaryVersion BinaryDeserializer::read(Type &customType, BinaryVersion version)
248{
249 return readCustomType(*this, customType, version);
250}
251
252inline BinarySerializer::BinarySerializer(std::ostream *stream)
253 : CppUtilities::BinaryWriter(stream)
254{
255}
256
257template <typename Type, Traits::EnableIf<Traits::IsSpecializationOf<Type, std::pair>> *> void BinarySerializer::write(const Type &pair)
258{
259 write(pair.first);
260 write(pair.second);
261}
262
263template <typename Type, Traits::EnableIf<Traits::IsSpecializingAnyOf<Type, std::unique_ptr, std::optional>> *>
264void BinarySerializer::write(const Type &opt)
265{
266 writeBool(static_cast<bool>(opt));
267 if (opt) {
268 write(*opt);
269 }
270}
271
272template <typename Type, Traits::EnableIf<Traits::IsSpecializingAnyOf<Type, std::shared_ptr>> *> void BinarySerializer::write(const Type &pointer)
273{
274 if (pointer == nullptr) {
275 writeByte(0);
276 return;
277 }
278 const auto id = reinterpret_cast<std::uintptr_t>(pointer.get());
279 const auto bigId = id >= 0x80000000000000;
280 auto &alreadyWritten = m_pointer[id];
281 std::uint8_t mode = alreadyWritten ? 2 : 1;
282 if (bigId) {
283 mode = mode | 0x4; // "flag" 3rd bit to indicate big ID
284 }
285 writeByte(mode);
286 if (bigId) {
287 writeUInt64BE(id);
288 } else {
289 writeVariableLengthUIntBE(id);
290 }
291 if (!alreadyWritten) {
292 alreadyWritten = true;
293 write(*pointer);
294 }
295}
296
297template <typename Type, Traits::EnableIf<IsIteratableExceptString<Type>, Traits::HasSize<Type>> *>
298void BinarySerializer::write(const Type &iteratable)
299{
300 writeVariableLengthUIntBE(iteratable.size());
301 for (const auto &element : iteratable) {
302 write(element);
303 }
304}
305
306template <typename Type, Traits::EnableIf<std::is_enum<Type>> *> void BinarySerializer::write(const Type &enumValue)
307{
308 write(static_cast<typename std::underlying_type<Type>::type>(enumValue));
309}
310
311template <typename Type, Traits::EnableIf<IsVariant<Type>> *> void BinarySerializer::write(const Type &variant)
312{
313 static_assert(std::variant_size_v<Type> < std::numeric_limits<std::uint8_t>::max(), "index will not exceed limit");
314 writeByte(static_cast<std::uint8_t>(variant.index()));
315 std::visit(
316 [this](const auto &valueOfActualType) {
317 if constexpr (!std::is_same_v<std::decay_t<decltype(valueOfActualType)>, std::monostate>) {
318 write(valueOfActualType);
319 } else {
320 CPP_UTILITIES_UNUSED(this)
321 }
322 },
323 variant);
324}
325
326template <typename Type, Traits::EnableIf<IsBuiltInType<Type>> *> void BinarySerializer::write(const Type &builtInType, BinaryVersion version)
327{
328 CPP_UTILITIES_UNUSED(version)
329 write(builtInType);
330}
331
332template <typename Type, Traits::EnableIf<IsCustomType<Type>> *> void BinarySerializer::write(const Type &customType, BinaryVersion version)
333{
334 writeCustomType(*this, customType, version);
335}
336
337} // namespace BinaryReflector
338} // namespace ReflectiveRapidJSON
339
340#endif // REFLECTIVE_RAPIDJSON_BINARY_REFLECTOR_H
The BinaryReflectorTests class tests the (de)serializer.
The BinaryDeserializer class can read various data types, including custom ones, from an std::istream...
Definition reflector.h:73
BinaryVersion read(Type &customType, BinaryVersion version=0)
The BinarySerializer class can write various data types, including custom ones, to an std::ostream.
Definition reflector.h:100
void write(const Type &customType, BinaryVersion version=0)
Traits::Any< Traits::IsAnyOf< Type, char, std::uint8_t, bool, std::string, std::int16_t, std::uint16_t, std::int32_t, std::uint32_t, std::int64_t, std::uint64_t, float, double >, Traits::IsIteratable< Type >, Traits::IsSpecializingAnyOf< Type, std::pair, std::unique_ptr, std::shared_ptr, std::optional >, std::is_enum< Type >, IsVariant< Type > > IsBuiltInType
Definition reflector.h:48
BinaryVersion readCustomType(BinaryDeserializer &deserializer, Type &customType, BinaryVersion version=0)
Reads customType via deserializer.
Traits::Not< IsBuiltInType< Type > > IsCustomType
Definition reflector.h:52
void writeCustomType(BinarySerializer &serializer, const Type &customType, BinaryVersion version=0)
Writes customType via serializer.
Traits::All< Traits::IsSpecializationOf< Type, std::variant > > IsVariant
Definition traits.h:57
Traits::Any< Traits::IsSpecializationOf< Type, std::multimap >, Traits::IsSpecializationOf< Type, std::unordered_multimap >, TreatAsMultiMapOrHash< Type > > IsMultiMapOrHash
Definition traits.h:40
std::uint64_t BinaryVersion
Definition reflector.h:38
The AdaptedBinarySerializable class allows considering 3rd party classes as serializable.
Definition reflector.h:33
static constexpr const char * qualifiedName
Definition reflector.h:35
static constexpr const char * name
Definition reflector.h:34
The BinarySerializable class provides the CRTP-base for (de)serializable objects.