Reflection for RapidJSON 0.0.16
Reflection for serializing/deserializing with RapidJSON
Loading...
Searching...
No Matches
binaryreflector.cpp
Go to the documentation of this file.
4
5#include <c++utilities/conversion/stringbuilder.h>
6#include <c++utilities/conversion/stringconversion.h>
7#include <c++utilities/io/misc.h>
8#include <c++utilities/tests/testutils.h>
9
10using CppUtilities::operator<<; // must be visible prior to the call site
11#include <cppunit/TestFixture.h>
12#include <cppunit/extensions/HelperMacros.h>
13
14#include <cstdint>
15#include <iostream>
16#include <limits>
17#include <map>
18#include <sstream>
19#include <string>
20#include <tuple>
21#include <unordered_map>
22#include <vector>
23
24using namespace std;
25using namespace CPPUNIT_NS;
26using namespace CppUtilities;
27using namespace CppUtilities::Literals;
28using namespace ReflectiveRapidJSON;
29
31
32// define some enums and structs for testing serialization
33enum SomeEnumBinary {
34 SomeEnumItem1,
35 SomeEnumItem2,
36 SomeEnumItem3,
37};
38
39enum class SomeEnumClassBinary : std::uint16_t {
40 Item1,
41 Item2,
42 Item3,
43};
44
45struct TestObjectBinary : public BinarySerializable<TestObjectBinary> {
46 int number;
47 double number2;
48 vector<int> numbers;
49 string text;
50 bool boolean;
51 map<string, int> someMap;
52 unordered_map<string, bool> someHash;
53 set<string> someSet;
54 multiset<string> someMultiset;
55 unordered_set<string> someUnorderedSet;
56 unordered_multiset<string> someUnorderedMultiset;
57 SomeEnumBinary someEnum;
58 SomeEnumClassBinary someEnumClass;
59 TimeSpan timeSpan;
60 DateTime dateTime;
61};
62
63struct NestingArrayBinary : public BinarySerializable<NestingArrayBinary> {
64 string name;
65 vector<TestObjectBinary> testObjects;
66};
67
68struct ObjectWithVariantsBinary : public BinarySerializable<ObjectWithVariantsBinary> {
69 variant<int, string, monostate> someVariant;
70 variant<string, float> anotherVariant;
71 variant<string, int> yetAnotherVariant;
72};
73
74// pretend serialization code for structs has been generated
75namespace ReflectiveRapidJSON {
76namespace BinaryReflector {
77
78template <> BinaryVersion readCustomType<TestObjectBinary>(BinaryDeserializer &deserializer, TestObjectBinary &customType, BinaryVersion version)
79{
80 CPP_UTILITIES_UNUSED(version)
81 deserializer.read(customType.number);
82 deserializer.read(customType.number2);
83 deserializer.read(customType.numbers);
84 deserializer.read(customType.text);
85 deserializer.read(customType.boolean);
86 deserializer.read(customType.someMap);
87 deserializer.read(customType.someHash);
88 deserializer.read(customType.someSet);
89 deserializer.read(customType.someMultiset);
90 deserializer.read(customType.someUnorderedSet);
91 deserializer.read(customType.someUnorderedMultiset);
92 deserializer.read(customType.someEnum);
93 deserializer.read(customType.someEnumClass);
94 deserializer.read(customType.timeSpan);
95 deserializer.read(customType.dateTime);
96 return 0;
97}
98
99template <> void writeCustomType<TestObjectBinary>(BinarySerializer &serializer, const TestObjectBinary &customType, BinaryVersion version)
100{
101 CPP_UTILITIES_UNUSED(version)
102 serializer.write(customType.number);
103 serializer.write(customType.number2);
104 serializer.write(customType.numbers);
105 serializer.write(customType.text);
106 serializer.write(customType.boolean);
107 serializer.write(customType.someMap);
108 serializer.write(customType.someHash);
109 serializer.write(customType.someSet);
110 serializer.write(customType.someMultiset);
111 serializer.write(customType.someUnorderedSet);
112 serializer.write(customType.someUnorderedMultiset);
113 serializer.write(customType.someEnum);
114 serializer.write(customType.someEnumClass);
115 serializer.write(customType.timeSpan);
116 serializer.write(customType.dateTime);
117}
118
119template <> BinaryVersion readCustomType<NestingArrayBinary>(BinaryDeserializer &deserializer, NestingArrayBinary &customType, BinaryVersion version)
120{
121 CPP_UTILITIES_UNUSED(version)
122 deserializer.read(customType.name);
123 deserializer.read(customType.testObjects);
124 return 0;
125}
126
127template <> void writeCustomType<NestingArrayBinary>(BinarySerializer &serializer, const NestingArrayBinary &customType, BinaryVersion version)
128{
129 CPP_UTILITIES_UNUSED(version)
130 serializer.write(customType.name);
131 serializer.write(customType.testObjects);
132}
133
134template <>
135BinaryVersion readCustomType<ObjectWithVariantsBinary>(BinaryDeserializer &deserializer, ObjectWithVariantsBinary &customType, BinaryVersion version)
136{
137 CPP_UTILITIES_UNUSED(version)
138 deserializer.read(customType.someVariant);
139 deserializer.read(customType.anotherVariant);
140 deserializer.read(customType.yetAnotherVariant);
141 return 0;
142}
143
144template <>
145void writeCustomType<ObjectWithVariantsBinary>(BinarySerializer &serializer, const ObjectWithVariantsBinary &customType, BinaryVersion version)
146{
147 CPP_UTILITIES_UNUSED(version)
148 serializer.write(customType.someVariant);
149 serializer.write(customType.anotherVariant);
150 serializer.write(customType.yetAnotherVariant);
151}
152
153} // namespace BinaryReflector
154
155// namespace BinaryReflector
156} // namespace ReflectiveRapidJSON
157
159
164class BinaryReflectorTests : public TestFixture {
165 CPPUNIT_TEST_SUITE(BinaryReflectorTests);
166 CPPUNIT_TEST(testSerializeSimpleStruct);
167 CPPUNIT_TEST(testDeserializeSimpleStruct);
168 CPPUNIT_TEST(testSerializeNestedStruct);
169 CPPUNIT_TEST(testDeserializeNestedStruct);
170 CPPUNIT_TEST(testSmallSharedPointer);
171 CPPUNIT_TEST(testBigSharedPointer);
172 CPPUNIT_TEST(testVariant);
173 CPPUNIT_TEST(testOptional);
174 CPPUNIT_TEST_SUITE_END();
175
176public:
178
179 void setUp() override;
180 void tearDown() override;
181
186 void assertTestObject(const TestObjectBinary &deserialized);
187 void testSharedPointer(std::uintptr_t fakePointer);
190 void testVariant();
191 void testOptional();
192
193private:
194 vector<unsigned char> m_buffer;
195 TestObjectBinary m_testObj;
196 NestingArrayBinary m_nestedTestObj;
197 vector<unsigned char> m_expectedTestObj;
198 vector<unsigned char> m_expectedNestedTestObj;
199};
200
202
203// clang-format off
205 : m_buffer()
206 , m_testObj()
207 , m_nestedTestObj()
208 , m_expectedTestObj({
209 0x00, 0x00, 0x00, 0x05,
210 0x40, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
211 0x85,
212 0x00, 0x00, 0x00, 0x01,
213 0x00, 0x00, 0x00, 0x02,
214 0x00, 0x00, 0x00, 0x03,
215 0x00, 0x00, 0x00, 0x04,
216 0x00, 0x00, 0x00, 0x05,
217 0x89,
218 0x73, 0x6F, 0x6D, 0x65, 0x20, 0x74, 0x65, 0x78, 0x74,
219 0x01,
220 0x82,
221 0x83, 0x62, 0x61, 0x72, 0x00, 0x00, 0x00, 0x13,
222 0x83, 0x66, 0x6f, 0x6f, 0x00, 0x00, 0x00, 0x11,
223 0x80,
224 0x83,
225 0x81, 0x31,
226 0x81, 0x32,
227 0x81, 0x33,
228 0x84,
229 0x81, 0x31,
230 0x81, 0x32,
231 0x81, 0x32,
232 0x81, 0x33,
233 0x80,
234 0x80,
235 0x00, 0x00, 0x00, 0x01,
236 0x00, 0x02,
237 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAB, 0xCD,
238 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEF, 0xAB,
239 })
240 , m_expectedNestedTestObj({
241 0x93, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x6e, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67,
242 0x82,
243 })
244{
245}
246// clang-format on
247
249{
250 m_testObj.number = 5;
251 m_testObj.number2 = 2.5;
252 m_testObj.numbers = { 1, 2, 3, 4, 5 };
253 m_testObj.text = "some text";
254 m_testObj.boolean = true;
255 m_testObj.someMap = {
256 { "foo", 17 },
257 { "bar", 19 },
258 };
259 m_testObj.someSet = { "1", "2", "3", "2" };
260 m_testObj.someMultiset = { "1", "2", "3", "2" };
261 m_testObj.someEnum = SomeEnumItem2;
262 m_testObj.someEnumClass = SomeEnumClassBinary::Item3;
263 m_testObj.timeSpan = TimeSpan(0xABCD);
264 m_testObj.dateTime = DateTime(0xEFAB);
265 m_nestedTestObj.name = "struct with nesting";
266 m_expectedNestedTestObj.reserve(m_expectedNestedTestObj.size() + 2 * m_expectedTestObj.size());
267 m_expectedNestedTestObj.insert(m_expectedNestedTestObj.end(), m_expectedTestObj.cbegin(), m_expectedTestObj.cend());
268 m_expectedNestedTestObj.insert(m_expectedNestedTestObj.end(), m_expectedTestObj.cbegin(), m_expectedTestObj.cend());
269 m_nestedTestObj.testObjects.insert(m_nestedTestObj.testObjects.end(), 2, m_testObj);
270}
271
275
276static void setBuffer(std::stringstream &stream, unsigned char *buffer, std::size_t bufferSize)
277{
278#if defined(__GLIBCXX__) && !defined(_LIBCPP_VERSION)
279 stream.rdbuf()->pubsetbuf(reinterpret_cast<char *>(buffer), static_cast<std::streamsize>(bufferSize));
280#else
281 CPP_UTILITIES_UNUSED(stream)
282 CPP_UTILITIES_UNUSED(buffer)
283 CPP_UTILITIES_UNUSED(bufferSize)
284#endif
285}
286
287static void readBuffer(std::stringstream &stream, unsigned char *buffer, std::size_t bufferSize)
288{
289#if defined(__GLIBCXX__) && !defined(_LIBCPP_VERSION)
290 CPP_UTILITIES_UNUSED(stream)
291 CPP_UTILITIES_UNUSED(buffer)
292 CPP_UTILITIES_UNUSED(bufferSize)
293#else
294 stream.read(reinterpret_cast<char *>(buffer), static_cast<std::streamsize>(bufferSize));
295#endif
296}
297static void writeBuffer(std::stringstream &stream, unsigned char *buffer, std::size_t bufferSize)
298{
299#if defined(__GLIBCXX__) && !defined(_LIBCPP_VERSION)
300 stream.rdbuf()->pubsetbuf(reinterpret_cast<char *>(buffer), static_cast<std::streamsize>(bufferSize));
301#else
302 stream.write(reinterpret_cast<const char *>(buffer), static_cast<std::streamsize>(bufferSize));
303#endif
304}
305
307{
308 stringstream stream(ios_base::out | ios_base::binary);
309 stream.exceptions(ios_base::failbit | ios_base::badbit);
310 m_buffer.resize(m_expectedTestObj.size());
311 setBuffer(stream, m_buffer.data(), m_buffer.size());
312 m_testObj.toBinary(stream);
313 readBuffer(stream, m_buffer.data(), m_buffer.size());
314
315 CPPUNIT_ASSERT_EQUAL(m_expectedTestObj, m_buffer);
316}
317
319{
320 stringstream stream(ios_base::in | ios_base::binary);
321 stream.exceptions(ios_base::failbit | ios_base::badbit);
322 writeBuffer(stream, m_expectedTestObj.data(), m_expectedTestObj.size());
323 const auto deserialized(TestObjectBinary::fromBinary(stream));
324 assertTestObject(deserialized);
325}
326
328{
329 stringstream stream(ios_base::out | ios_base::binary);
330 stream.exceptions(ios_base::failbit | ios_base::badbit);
331 m_buffer.resize(m_expectedNestedTestObj.size());
332 setBuffer(stream, m_buffer.data(), m_buffer.size());
333 m_nestedTestObj.toBinary(stream);
334 readBuffer(stream, m_buffer.data(), m_buffer.size());
335
336 CPPUNIT_ASSERT_EQUAL(m_expectedNestedTestObj, m_buffer);
337}
338
340{
341 stringstream stream(ios_base::in | ios_base::binary);
342 stream.exceptions(ios_base::failbit | ios_base::badbit);
343 writeBuffer(stream, m_expectedNestedTestObj.data(), m_expectedNestedTestObj.size());
344
345 const auto deserialized(NestingArrayBinary::fromBinary(stream));
346 CPPUNIT_ASSERT_EQUAL(m_nestedTestObj.name, deserialized.name);
347 for (const auto &testObj : deserialized.testObjects) {
348 assertTestObject(testObj);
349 }
350}
351
352void BinaryReflectorTests::assertTestObject(const TestObjectBinary &deserialized)
353{
354 CPPUNIT_ASSERT_EQUAL(m_testObj.number, deserialized.number);
355 CPPUNIT_ASSERT_EQUAL(m_testObj.number2, deserialized.number2);
356 CPPUNIT_ASSERT_EQUAL(m_testObj.numbers, deserialized.numbers);
357 CPPUNIT_ASSERT_EQUAL(m_testObj.text, deserialized.text);
358 CPPUNIT_ASSERT_EQUAL(m_testObj.boolean, deserialized.boolean);
359 CPPUNIT_ASSERT_EQUAL(m_testObj.someMap, deserialized.someMap);
360 CPPUNIT_ASSERT_EQUAL(m_testObj.someHash, deserialized.someHash);
361 CPPUNIT_ASSERT_EQUAL(m_testObj.someSet, deserialized.someSet);
362 CPPUNIT_ASSERT_EQUAL(m_testObj.someMultiset, deserialized.someMultiset);
363 CPPUNIT_ASSERT_EQUAL(m_testObj.someUnorderedSet, deserialized.someUnorderedSet);
364 CPPUNIT_ASSERT_EQUAL(m_testObj.someUnorderedMultiset, deserialized.someUnorderedMultiset);
365}
366
368{
369 // create a shared pointer for the fake pointer ensuring that it is not actually deleted
370 shared_ptr<int> sharedPointer(reinterpret_cast<int *>(fakePointer), [](int *) {});
371
372 // setup stream
373 stringstream stream(ios_base::in | ios_base::out | ios_base::binary);
374 stream.exceptions(ios_base::failbit | ios_base::badbit);
375
376 // serialize the shared pointer assuming its contents have been written before (to prevent actually dereferencing it)
377 BinaryReflector::BinarySerializer serializer(&stream);
378 serializer.m_pointer[fakePointer] = true;
379 serializer.write(sharedPointer);
380
381 // deserialize the shared pointer assuming it has already been read and the type does not match
382 BinaryReflector::BinaryDeserializer deserializer(&stream);
383 shared_ptr<int> readPtr;
384 deserializer.m_pointer[fakePointer] = "foo";
385 CPPUNIT_ASSERT_THROW(deserializer.read(readPtr), CppUtilities::ConversionException);
386 CPPUNIT_ASSERT(readPtr == nullptr);
387
388 // deserialize the shared pointer assuming it has already been read and the type matches
389 stream.seekg(0);
390 deserializer.m_pointer[fakePointer] = make_shared<int>(42);
391 deserializer.read(readPtr);
392 CPPUNIT_ASSERT(readPtr != nullptr);
393 CPPUNIT_ASSERT_EQUAL(42, *readPtr);
394}
395
397{
398 testSharedPointer(std::numeric_limits<std::uintptr_t>::min() + 1);
399}
400
402{
403 testSharedPointer(std::numeric_limits<std::uintptr_t>::max());
404}
405
407{
408 // create test object
409 ObjectWithVariantsBinary variants;
410 variants.someVariant = std::monostate{};
411 variants.anotherVariant = "foo";
412 variants.yetAnotherVariant = 42;
413
414 // serialize test object
415 stringstream stream(ios_base::in | ios_base::out | ios_base::binary);
416 stream.exceptions(ios_base::failbit | ios_base::badbit);
417 variants.toBinary(stream);
418
419 // deserialize the object again
420 const auto deserializedVariants = ObjectWithVariantsBinary::fromBinary(stream);
421
422 CPPUNIT_ASSERT_EQUAL(2_st, deserializedVariants.someVariant.index());
423 CPPUNIT_ASSERT_EQUAL(0_st, deserializedVariants.anotherVariant.index());
424 CPPUNIT_ASSERT_EQUAL(1_st, deserializedVariants.yetAnotherVariant.index());
425 CPPUNIT_ASSERT_EQUAL("foo"s, get<0>(deserializedVariants.anotherVariant));
426 CPPUNIT_ASSERT_EQUAL(42, get<1>(deserializedVariants.yetAnotherVariant));
427}
428
430{
431 // create test objects
432 const auto str = std::make_optional<std::string>("foo");
433 const auto nullStr = std::optional<std::string>();
434
435 // serialize test object
436 auto stream = std::stringstream(std::ios_base::in | std::ios_base::out | std::ios_base::binary);
437 stream.exceptions(std::ios_base::failbit | std::ios_base::badbit);
438 auto ser = BinaryReflector::BinarySerializer(&stream);
439 ser.write(str);
440 ser.write(nullStr);
441
442 // deserialize the object again
443 auto deser = BinaryReflector::BinaryDeserializer(&stream);
444 auto deserStr = std::optional<std::string>();
445 auto deserNullStr = std::optional<std::string>();
446 deser.read(deserStr);
447 deser.read(deserNullStr);
448
449 CPPUNIT_ASSERT(deserStr.has_value());
450 CPPUNIT_ASSERT_EQUAL("foo"s, deserStr.value());
451 CPPUNIT_ASSERT(!nullStr.has_value());
452}
Contains functions for (de)serializing objects from the chrono utilities provided by the C++ utilitie...
Contains BinaryReader and BinaryWriter supporting binary (de)serialization of primitive and custom ty...
Contains only the definition of the BinarySerializable template class which makes the reflection acce...
CPPUNIT_TEST_SUITE_REGISTRATION(BinaryReflectorTests)
The BinaryReflectorTests class tests the (de)serializer.
void testSharedPointer(std::uintptr_t fakePointer)
void assertTestObject(const TestObjectBinary &deserialized)
The BinaryDeserializer class can read various data types, including custom ones, from an std::istream...
Definition reflector.h:73
The BinarySerializer class can write various data types, including custom ones, to an std::ostream.
Definition reflector.h:100
BinaryVersion readCustomType(BinaryDeserializer &deserializer, Type &customType, BinaryVersion version=0)
Reads customType via deserializer.
void writeCustomType(BinarySerializer &serializer, const Type &customType, BinaryVersion version=0)
Writes customType via serializer.
std::uint64_t BinaryVersion
Definition reflector.h:38
The BinarySerializable class provides the CRTP-base for (de)serializable objects.