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