mirror of
https://github.com/fraillt/bitsery.git
synced 2026-06-18 21:29:05 +00:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a6dad0885f | ||
|
|
65f90637df | ||
|
|
b10f86da00 | ||
|
|
6c3e1aee43 | ||
|
|
e5f8d5742f | ||
|
|
a2ecf8d7b0 | ||
|
|
670130397b | ||
|
|
4a0b3cae98 | ||
|
|
b3b32ab393 | ||
|
|
6ebdb9915b | ||
|
|
2e62bd08e3 | ||
|
|
54f69a5eea | ||
|
|
275c4138ee |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,5 +1,5 @@
|
||||
.idea/
|
||||
.vs/
|
||||
build/
|
||||
cmake-build-debug/
|
||||
cmake-build-*
|
||||
CTestConfig.cmake
|
||||
|
||||
90
CHANGELOG.md
90
CHANGELOG.md
@@ -1,3 +1,91 @@
|
||||
# [4.5.1](https://github.com/fraillt/bitsery/compare/v4.5.0...v4.5.1) (2019-01-16)
|
||||
|
||||
### Improvements
|
||||
* template specializations, where possible, was changed to avoid using variadics, some Visual Studio compilers has [issues](https://developercommunity.visualstudio.com/content/problem/3437/error-with-c11-variadics.html) with variadic templates.
|
||||
* reduced compile warnings for VisualStudio:
|
||||
* added explicit casts
|
||||
* renamed `struct` to `class` where class is used as friend. e.g. `friend class bitsery::Access`, because it is more conventional usage.
|
||||
|
||||
# [4.5.0](https://github.com/fraillt/bitsery/compare/v4.4.0...v4.5.0) (2019-01-10)
|
||||
|
||||
### Features
|
||||
* ability to create non default constructible objects, by defining private default constructor and making `friend class bitsery::Access;` to access it.
|
||||
It is not necessary to enforce class invariant immediately, because internal object representation will be overriden anyway.
|
||||
|
||||
### Improvements
|
||||
* `StdSmartPtr` supports `std::unique_ptr` with custom deleter.
|
||||
* `*InputBufferAdapter`(all) can also accept const buffer;
|
||||
|
||||
### Bug fixes
|
||||
* fixed deserialization in `bitsery/ext/std_map{set}` when target container is not empty.
|
||||
* added missing template parameters for specializations on `std` containers in multiple files in `bitsery/ext/*`.
|
||||
|
||||
# [4.4.0](https://github.com/fraillt/bitsery/compare/v4.3.0...v4.4.0) (2019-01-08)
|
||||
|
||||
### Features
|
||||
* new extensions **CompactValue** and **CompactValueAsObject**, stores integral values in less space if possible. This is useful when you're working with mostly small values, that in rare cases can be large.
|
||||
E.g. `int64_t money = 8000;` will only use 2 bytes, instead of 8. **CompactValueAsObject** allows to use `ext()` overload, without specifying size of underlying type and sets BUFFER_OVERFLOW error if value doesn't fit in underlying type during deserialization.
|
||||
|
||||
### Improvements
|
||||
* improved **PolymorphicContext**, allows to extend already registered hierarchy in one translation unit, using different type other than `PolymorphicBaseClass` to avoid symbol collision between translation units or libraries.
|
||||
`registerBasesList` was modified, so that it could accept user defined type (instead of `PolymorphicBaseClass`) that is used to declare hierarchy, by default it is `PolymorphicBaseClass`.
|
||||
This introduced breaking change, for those who used this syntax (`registerBasesList<MySerializer, Shape>({})`) during registration.
|
||||
It is encouraged to define helper type, that could be used for registering hierarchy for serialization and deserialization [example](examples/smart_pointers_with_polymorphism.cpp).
|
||||
`This is only relevant then you want to use **PolymorphicContext** between different translation units or libraries`.
|
||||
```cpp
|
||||
//libA
|
||||
namespace bitsery {
|
||||
namespace ext {
|
||||
template<>
|
||||
struct PolymorphicBaseClass<Shape> : PolymorphicDerivedClasses<Circle, Rectangle> {};
|
||||
}
|
||||
}
|
||||
using MyPolymorphicClassesForRegistering = bitsery::ext::PolymorphicClassesList<Shape>;
|
||||
...
|
||||
ctx.registerBasesList<MySerializer>(MyPolymorphicClassesForRegistering{}).
|
||||
|
||||
//otherLib
|
||||
struct MySquare: Shape {...}
|
||||
//now it must define different type (exactly the same as PolymorphicBaseClass) to declare hierarchy
|
||||
template<typename TBase>
|
||||
struct MyHierarchy {
|
||||
using Childs = PolymorphicClassesList<>;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct MyHierarchy<Shape>: bitsery::ext::bitsery::ext::PolymorphicClassesList<MySquare> {};
|
||||
...
|
||||
//notice that we pass MyHierarchy as second argument
|
||||
ctx.registerBasesList<MySerializer, MyHierarchy>(MyPolymorphicClassesForRegistering{}).
|
||||
```
|
||||
* **PolymorphicContext** also get optional method `registerSingleBaseBranch`, that allows manually register hierarchies, this might be more convenient when using you need to register in different translation units (or libraries), but it is error-prone.
|
||||
|
||||
# [4.3.0](https://github.com/fraillt/bitsery/compare/v4.2.1...v4.3.0) (2018-08-23)
|
||||
|
||||
### Features
|
||||
|
||||
* added runtime polymorphism support for pointer like types (raw and smart pointers).
|
||||
In order to enable polymorphism new **PolymorphicContext** was created. It provides capability to register classes with serializer/deserializer.
|
||||
* runtime polymorphism can be customized, by replacing **StandardRTTI** from <bitsery/ext/utils/rtti_utils.h> header.
|
||||
* added smart pointers support for std::unique_ptr, std::shared_ptr and std::weak_ptr via **StdSmartPtr** extension.
|
||||
* new **UnsafeInputBufferAdapter** doesn't check for buffer size on deserialization, on some compilers can improve deserialization performance up to ~40%.
|
||||
|
||||
### Improvements
|
||||
* creatly improved interface for extending/implementing support for pointer like types. Now all pointer like types extends from **PointerObjectExtensionBase** and implements/configures required details.
|
||||
* reimplemented **PointerOwner**, **PointerObserver**, **ReferencedByPointer**.
|
||||
* reimplemented **PointerLinkingContext** to properly support shared objects and runtime polymorphism, pointer ownership for shared objects now has two states: SharedOwner e.g. std::shared_ptr and SharedObserver std::weak_ptr.
|
||||
|
||||
### Other notes
|
||||
There is one *minor?* issue/limitation for pointer like types that uses virtual inheritance. When several pointers points to same object through different static type. it will not work correctly e.g.:
|
||||
```cpp
|
||||
struct Derived: virtual Base {...};
|
||||
struct MyData {
|
||||
std::shared_ptr<Derived> sptr;
|
||||
std::weak_ptr<Base> wptddr;
|
||||
}
|
||||
```
|
||||
In this example wptr and sptr have different static type, and *Derived* is virtually inherited from *Base*, so I get different pointer address for different types.
|
||||
|
||||
# [4.2.1](https://github.com/fraillt/bitsery/compare/v4.2.0...v4.2.1) (2018-03-09)
|
||||
|
||||
### Improvements
|
||||
@@ -106,7 +194,7 @@ Be careful when using deserializing untrusted data and make sure to enforce fund
|
||||
### Features
|
||||
|
||||
* refactored interface, now works with C++11 compiler.
|
||||
* new new extension **Growable**, that allows to have forward/backward compatability within this functions serialization flow. It only allows to append new data at the end of to existing flow without breaking old consumers.
|
||||
* new extension **Growable**, that allows to have forward/backward compatability within this functions serialization flow. It only allows to append new data at the end of to existing flow without breaking old consumers.
|
||||
* old consumer: correctly read old interfce and ignore new data.
|
||||
* new consumer: get defaults (zero values) for new fields, when reading old data.
|
||||
* added new extension for associative *map* containers **ContainerMap**.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
cmake_minimum_required(VERSION 3.1)
|
||||
project(bitsery
|
||||
LANGUAGES CXX
|
||||
VERSION 4.2.1)
|
||||
VERSION 4.5.1)
|
||||
|
||||
#======== build options ===================================
|
||||
option(BITSERY_BUILD_EXAMPLES "Build examples" OFF)
|
||||
@@ -41,13 +41,6 @@ install(DIRECTORY include/bitsery
|
||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
|
||||
|
||||
#================ handle sub-projects =====================
|
||||
# examples and tests are independend directories, that can be built separatelly.
|
||||
# make find_package to no-op, when searching for "Bitsery", because it is created here.
|
||||
macro(find_package)
|
||||
if (NOT ${ARGV0} STREQUAL Bitsery)
|
||||
_find_package(${ARGV})
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
if (BITSERY_BUILD_EXAMPLES)
|
||||
message("build bitsery examples")
|
||||
|
||||
@@ -26,6 +26,16 @@ you contribute:
|
||||
6. Open a pull request against Bitsery *master* branch. Currently ongoing development is on *master*. At some point an integration branch will be set-up, and pull-requests should target that, but for now its all against master. You may see feature branches come and go, too.
|
||||
|
||||
|
||||
If you're working with visual studio, there is how to build and run all tests from command line
|
||||
```shell
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DBITSERY_BUILD_TESTS=ON -DGTEST_ROOT="<PATH to GTEST>" -DCMAKE_CXX_FLAGS_RELEASE=/MT ..
|
||||
cmake --build . --config Release
|
||||
(cd tests && ctest -C Release && cd ..)
|
||||
```
|
||||
/MT option might be optional, depending on how gtest was built.
|
||||
|
||||
## Style guide
|
||||
|
||||
Just use your own judgment and stick to the style of the surrounding code.
|
||||
Just use your own judgment and stick to the style of the surrounding code.
|
||||
|
||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 Mindaugas Vinkelis
|
||||
Copyright (c) 2018 Mindaugas Vinkelis
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -19,7 +19,7 @@ All cross-platform requirements are enforced at compile time, so serialized data
|
||||
* Can read/write from any source: stream (file, network stream. etc... ), or buffer (vector, c-array, etc...).
|
||||
* Don't pay for what you don't use! - customize your serialization via **extensions**. Some notable *extensions* allow:
|
||||
* forward/backward compatibility for your types.
|
||||
* raw pointers (no polymorphism yet).
|
||||
* smart and raw pointers with customizable runtime polymorphism support.
|
||||
* fine-grained bit-level serialization control.
|
||||
* Easily extendable for any type.
|
||||
* Allows flexible or/and verbose syntax for better serialization control.
|
||||
@@ -105,4 +105,4 @@ This library was tested on
|
||||
|
||||
## License
|
||||
|
||||
**bitsery** is licensed under the [MIT license](LICENSE).
|
||||
**bitsery** is licensed under the [MIT license](LICENSE).
|
||||
|
||||
@@ -11,34 +11,38 @@ Library design:
|
||||
* `forward/backward compatibility via Growable extension`
|
||||
* `pointers`
|
||||
* `inheritance`
|
||||
* `polymorphism`
|
||||
|
||||
|
||||
Core Serializer/Deserializer functions (alphabetical order):
|
||||
* `align`
|
||||
* `boolValue`
|
||||
* `container`
|
||||
* `ext`
|
||||
* `context`
|
||||
* `context<T>`
|
||||
* `contextOrNull<T>`
|
||||
* `object`
|
||||
* `text`
|
||||
* `value`
|
||||
* `align` (1.0.0)
|
||||
* `boolValue` (4.0.0)
|
||||
* `container` (1.0.0)
|
||||
* `ext` (2.0.0)
|
||||
* `context` (3.0.0)
|
||||
* `context<T>` (4.1.0)
|
||||
* `contextOrNull<T>` (4.2.0)
|
||||
* `object` (1.0.0)
|
||||
* `text` (1.0.0)
|
||||
* `value` (1.0.0)
|
||||
|
||||
Serializer/Deserializer extensions via `ext` method (alphabetical order):
|
||||
* `BaseClass`
|
||||
* `Entropy`
|
||||
* `Growable`
|
||||
* `PointerOwner`
|
||||
* `PointerObserver`
|
||||
* `ReferencedByPointer`
|
||||
* `StdMap`
|
||||
* `StdOptional`
|
||||
* `StdQueue`
|
||||
* `StdSet`
|
||||
* `StdStack`
|
||||
* `ValueRange`
|
||||
* `VirtualBaseClass`
|
||||
* `BaseClass` (4.2.0)
|
||||
* `CompactValue` (4.4.0)
|
||||
* `CompactValueAsObject` (4.4.0)
|
||||
* `Entropy` (3.0.0)
|
||||
* `Growable` (3.0.0)
|
||||
* `PointerOwner` (4.1.0)
|
||||
* `PointerObserver` (4.1.0)
|
||||
* `ReferencedByPointer` (4.1.0)
|
||||
* `StdMap` (3.0.0)
|
||||
* `StdOptional` (2.0.0)
|
||||
* `StdQueue` (4.0.0)
|
||||
* `StdSet` (4.0.0)
|
||||
* `StdSmartPrt` (4.3.0)
|
||||
* `StdStack` (4.0.0)
|
||||
* `ValueRange` (3.0.0)
|
||||
* `VirtualBaseClass` (4.2.0)
|
||||
|
||||
AdapterWriter/Reader functions:
|
||||
* `writeBits/readBits`
|
||||
@@ -65,7 +69,7 @@ Output adapters (buffer and stream) functions:
|
||||
|
||||
|
||||
Tips and tricks:
|
||||
* if you're getting static assert "please define 'serialize' function", most likely it is because your **serialize** function is not defined in same namespace as object.
|
||||
* if you're getting static assert "please define 'serialize' function", please define **serialize** function in same namespace as object, or in **bitsery** namespace, for more info [ADL](https://en.cppreference.com/w/cpp/language/adl).
|
||||
|
||||
Other:
|
||||
* [Contributing](../CONTRIBUTING.md)
|
||||
|
||||
@@ -38,5 +38,8 @@ foreach(ExampleFile ${ExampleFiles})
|
||||
get_filename_component(ExampleName ${ExampleFile} NAME_WE)
|
||||
add_executable(bitsery.example.${ExampleName} ${ExampleFile})
|
||||
target_link_libraries(bitsery.example.${ExampleName} PRIVATE Bitsery::bitsery)
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
|
||||
target_compile_options(bitsery.example.${ExampleName} PRIVATE -Wextra -Wno-missing-braces -Wpedantic -Weffc++)
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
|
||||
@@ -70,6 +70,10 @@ using InputAdapter = InputBufferAdapter<Buffer>;
|
||||
// s.template context<MyContext>();
|
||||
using Context = std::tuple<int, std::pair<uint32_t, uint32_t>>;
|
||||
|
||||
//NOTE:
|
||||
// if your context has no additional usage outside of serialization flow,
|
||||
// then you can create it internally via configuration (see inheritance.cpp)
|
||||
|
||||
int main() {
|
||||
|
||||
MyTypes::GameState data{};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
//this example coverls all the corner cases that can happen using inherintace
|
||||
//this example covers all the corner cases that can happen using inheritance
|
||||
//in reality virtual inherintance is usually avoided, so your code would look much simpler.
|
||||
//
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
//this header contains two extensions, that specifies inheritance type of base class
|
||||
// BaseClass - normal inheritance
|
||||
// VirtualBaseClass - when virtual inheritance is used
|
||||
//in order for virtual inheritance to work, InheritanceContext is required.
|
||||
//in order for virtual inheritance to work, InheritanceContext is required. for normal inheritance it is not required
|
||||
//it can be created either internally (via configuration) or externally (pointer to context).
|
||||
#include <bitsery/ext/inheritance.h>
|
||||
|
||||
|
||||
65
examples/non_default_constructible.cpp
Normal file
65
examples/non_default_constructible.cpp
Normal file
@@ -0,0 +1,65 @@
|
||||
//
|
||||
// example of how to deserialize non default constructible objects
|
||||
//
|
||||
|
||||
#include <bitsery/bitsery.h>
|
||||
#include <bitsery/adapter/buffer.h>
|
||||
#include <bitsery/traits/vector.h>
|
||||
|
||||
class MyData {
|
||||
//define your private data
|
||||
float _x{0};
|
||||
float _y{0};
|
||||
//make bitsery:Access friend
|
||||
friend class bitsery::Access;
|
||||
//create default constructor, don't worry about class invariant, it will be restored in deserialization
|
||||
MyData() = default;
|
||||
//define serialize function
|
||||
|
||||
template <typename S>
|
||||
void serialize(S& s) {
|
||||
s.value4b(_x);
|
||||
s.value4b(_y);
|
||||
}
|
||||
public:
|
||||
//define non default public constructor
|
||||
MyData(float x, float y):_x{x}, _y{y} {}
|
||||
//this is for convenience
|
||||
bool operator ==(const MyData&rhs) const {
|
||||
return _x == rhs._x && _y == rhs._y;
|
||||
}
|
||||
};
|
||||
|
||||
using namespace bitsery;
|
||||
|
||||
//some helper types
|
||||
using Buffer = std::vector<uint8_t>;
|
||||
using OutputAdapter = OutputBufferAdapter<Buffer>;
|
||||
using InputAdapter = InputBufferAdapter<Buffer>;
|
||||
|
||||
int main() {
|
||||
|
||||
//initialize our data
|
||||
std::vector<MyData> data{};
|
||||
data.emplace_back(145.4f, 84.48f);
|
||||
std::vector<MyData> res{};
|
||||
|
||||
//we cant use quick (de)serialization helper methods, because we ant to serialize container directly
|
||||
//create buffer
|
||||
Buffer buffer{};
|
||||
|
||||
//create and serialize container, and get written bytes count
|
||||
Serializer<OutputAdapter> ser{OutputAdapter{buffer}};
|
||||
ser.container(data, 10);
|
||||
auto writtenBytes = AdapterAccess::getWriter(ser).writtenBytesCount();
|
||||
|
||||
//create and deserialize container
|
||||
Deserializer<InputAdapter> des{InputAdapter{buffer.begin(), writtenBytes}};
|
||||
des.container(res, 10);
|
||||
|
||||
//check if everything went ok
|
||||
auto& reader = AdapterAccess::getReader(des);
|
||||
assert(reader.error() == ReaderError::NoError && reader.isCompletedSuccessfully());
|
||||
assert(res == data);
|
||||
}
|
||||
|
||||
273
examples/smart_pointers_with_polymorphism.cpp
Normal file
273
examples/smart_pointers_with_polymorphism.cpp
Normal file
@@ -0,0 +1,273 @@
|
||||
//
|
||||
// Created by fraillt on 18.4.26.
|
||||
//
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
#include <bitsery/bitsery.h>
|
||||
#include <bitsery/traits/vector.h>
|
||||
#include <bitsery/adapter/buffer.h>
|
||||
#include <bitsery/ext/pointer.h>
|
||||
#include <bitsery/ext/inheritance.h>
|
||||
#include <bitsery/ext/std_smart_ptr.h>
|
||||
|
||||
//in order to work with polymorphic types, we need to describe few steps:
|
||||
// 1) describe relationships between base and derived types
|
||||
// this will allow to know what are possible types reachable from base class
|
||||
// 2) bind serializer to base class
|
||||
// this will allow to iterate through all types, and add serialization functions,
|
||||
// without this step compiler would simply remove functions that are not bound at compile-time even it we use type at runtime.
|
||||
|
||||
using bitsery::ext::BaseClass;
|
||||
|
||||
using bitsery::ext::PointerObserver;
|
||||
using bitsery::ext::StdSmartPtr;
|
||||
|
||||
//define our data structures
|
||||
struct Color {
|
||||
float r{}, g{}, b{};
|
||||
bool operator == (const Color& o) const {
|
||||
return std::tie(r, g, b) ==
|
||||
std::tie(o.r, o.g, o.b);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
struct Shape {
|
||||
Color clr{};
|
||||
virtual ~Shape() = 0;
|
||||
};
|
||||
|
||||
Shape::~Shape() = default;
|
||||
|
||||
struct Circle : Shape {
|
||||
int32_t radius{};
|
||||
bool operator == (const Circle& o) const {
|
||||
return std::tie(radius, clr) ==
|
||||
std::tie(o.radius, o.clr);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
struct Rectangle : Shape {
|
||||
int32_t width{};
|
||||
int32_t height{};
|
||||
bool operator == (const Rectangle& o) const {
|
||||
return std::tie(width, height, clr) ==
|
||||
std::tie(o.width, o.height, o.clr);
|
||||
}
|
||||
};
|
||||
|
||||
struct RoundedRectangle : Rectangle {
|
||||
int32_t radius{};
|
||||
bool operator == (const RoundedRectangle& o) const {
|
||||
return std::tie(radius, static_cast<const Rectangle&>(*this)) ==
|
||||
std::tie(o.radius, static_cast<const Rectangle&>(o));
|
||||
}
|
||||
};
|
||||
|
||||
//define serialization functions
|
||||
template<typename S>
|
||||
void serialize(S &s, Color &o) {
|
||||
//in real world scenario, it might be possible to serialize this using ValueRange, to map values in smaller space
|
||||
//but for the sake of this example keep it simple
|
||||
s.value4b(o.r);
|
||||
s.value4b(o.g);
|
||||
s.value4b(o.b);
|
||||
}
|
||||
|
||||
template<typename S>
|
||||
void serialize(S &s, Shape &o) {
|
||||
s.object(o.clr);
|
||||
}
|
||||
|
||||
template<typename S>
|
||||
void serialize(S &s, Circle &o) {
|
||||
s.ext(o, bitsery::ext::BaseClass<Shape>{});
|
||||
s.value4b(o.radius);
|
||||
}
|
||||
|
||||
template<typename S>
|
||||
void serialize(S &s, Rectangle &o) {
|
||||
s.ext(o, bitsery::ext::BaseClass<Shape>{});
|
||||
s.value4b(o.width);
|
||||
s.value4b(o.height);
|
||||
}
|
||||
|
||||
template<typename S>
|
||||
void serialize(S &s, RoundedRectangle &o) {
|
||||
s.ext(o, bitsery::ext::BaseClass<Rectangle>{});
|
||||
s.value4b(o.radius);
|
||||
}
|
||||
|
||||
//define our test structure
|
||||
struct SomeShapes {
|
||||
std::vector<std::shared_ptr<Shape>> sharedList;
|
||||
std::unique_ptr<Shape> uniquePtr;
|
||||
//weak ptr and refPtr will point to sharedList
|
||||
std::weak_ptr<Shape> weakPtr;
|
||||
Shape* refPtr;
|
||||
};
|
||||
|
||||
//creates object, and populates some data
|
||||
SomeShapes createData() {
|
||||
SomeShapes data{};
|
||||
{
|
||||
auto tmp = new RoundedRectangle{};
|
||||
tmp->height = 151572;
|
||||
tmp->width = 488795;
|
||||
tmp->radius = 898;
|
||||
tmp->clr.r = 0.5f;
|
||||
tmp->clr.g = 1.0f;
|
||||
tmp->clr.b = 1.0f;
|
||||
data.uniquePtr.reset(tmp);
|
||||
}
|
||||
{
|
||||
auto tmp = new Circle{};
|
||||
tmp->radius = 75987;
|
||||
tmp->clr.r = 0.5f;
|
||||
tmp->clr.g = 0.0f;
|
||||
tmp->clr.b = 1.0f;
|
||||
data.sharedList.emplace_back(tmp);
|
||||
}
|
||||
{
|
||||
auto tmp = new Rectangle{};
|
||||
tmp->height = 15157;
|
||||
tmp->width = 48879;
|
||||
tmp->clr.r = 1.0f;
|
||||
tmp->clr.g = 0.0f;
|
||||
tmp->clr.b = 0.0f;
|
||||
data.sharedList.emplace_back(tmp);
|
||||
}
|
||||
data.weakPtr = data.sharedList[0];
|
||||
data.refPtr = data.sharedList[1].get();
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
template<typename S>
|
||||
void serialize(S &s, SomeShapes &o) {
|
||||
s.ext(o.uniquePtr, StdSmartPtr{});
|
||||
// to make things more interesting first serialize weakPtr and refPtr,
|
||||
// even though objects that weakPtr and refPtr is serialized later,
|
||||
// bitsery will work regardless
|
||||
s.ext(o.weakPtr, StdSmartPtr{});
|
||||
s.ext(o.refPtr, PointerObserver{});
|
||||
s.container(o.sharedList, 100, [&s](std::shared_ptr<Shape> &item) {
|
||||
s.ext(item, StdSmartPtr{});
|
||||
});
|
||||
}
|
||||
|
||||
// STEP 1
|
||||
// define relationships between base and derived classes
|
||||
|
||||
namespace bitsery {
|
||||
namespace ext {
|
||||
|
||||
//for each base class define DIRECTLY derived classes
|
||||
//e.g. PolymorphicBaseClass<Shape> : PolymorphicDerivedClasses<Circle, Rectangle, RoundedRectangle>
|
||||
// is incorrect, because RoundedRectangle does not directly derive from Shape
|
||||
template<>
|
||||
struct PolymorphicBaseClass<Shape> : PolymorphicDerivedClasses<Circle, Rectangle> {
|
||||
};
|
||||
|
||||
template<>
|
||||
struct PolymorphicBaseClass<Rectangle> : PolymorphicDerivedClasses<RoundedRectangle> {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// convenient type that stores all our types, so that we could easily register and
|
||||
// also it automatically ensures, that classes is registered in the same order for serialization and deserialization
|
||||
using MyPolymorphicClassesForRegistering = bitsery::ext::PolymorphicClassesList<Shape>;
|
||||
|
||||
|
||||
//use bitsery namespace for convenience
|
||||
using namespace bitsery;
|
||||
|
||||
//some helper types
|
||||
using Buffer = std::vector<uint8_t>;
|
||||
using OutputAdapter = OutputBufferAdapter<Buffer>;
|
||||
using InputAdapter = InputBufferAdapter<Buffer>;
|
||||
|
||||
//we need to define few things in order to work with polymorphism
|
||||
//1) we need pointer linking context to work with pointers
|
||||
//2) we need polymorphic context to be able to work with polymorphic types
|
||||
using TContext = std::tuple<ext::PointerLinkingContext, ext::PolymorphicContext<ext::StandardRTTI>>;
|
||||
//NOTE:
|
||||
// RTTI can be customizable, if you can't use dynamic_cast and typeid, and have 'custom' solution
|
||||
|
||||
using MySerializer = BasicSerializer<AdapterWriter<OutputAdapter, DefaultConfig>, TContext>;
|
||||
using MyDeserializer = BasicDeserializer<AdapterReader<InputAdapter, DefaultConfig>, TContext>;
|
||||
|
||||
|
||||
//checks if deserialized data is equal
|
||||
void assertSameShapes(const SomeShapes &data, const SomeShapes &res) {
|
||||
{
|
||||
auto d = dynamic_cast<RoundedRectangle *>(data.uniquePtr.get());
|
||||
auto r = dynamic_cast<RoundedRectangle *>(res.uniquePtr.get());
|
||||
assert(r != nullptr);
|
||||
assert(*d == *r);
|
||||
}
|
||||
{
|
||||
auto d = dynamic_cast<Circle *>(data.sharedList[0].get());
|
||||
auto r = dynamic_cast<Circle *>(res.sharedList[0].get());
|
||||
assert(r != nullptr);
|
||||
assert(*d == *r);
|
||||
}
|
||||
{
|
||||
auto d = dynamic_cast<Rectangle *>(data.sharedList[1].get());
|
||||
auto r = dynamic_cast<Rectangle *>(res.sharedList[1].get());
|
||||
assert(r != nullptr);
|
||||
assert(*d == *r);
|
||||
}
|
||||
assert(res.weakPtr.lock().get() == res.sharedList[0].get());
|
||||
assert(res.refPtr == res.sharedList[1].get());
|
||||
}
|
||||
|
||||
int main() {
|
||||
|
||||
auto data = createData();
|
||||
|
||||
//create buffer to store data
|
||||
Buffer buffer{};
|
||||
size_t writtenSize{};
|
||||
{
|
||||
//STEP 2
|
||||
//bind serializer with base polymorphic types, it will go through all reachable classes that is defined in first step.
|
||||
//so you dont need to add Rectangle to reach for RoundedRectangle
|
||||
TContext ctx{};
|
||||
std::get<1>(ctx).registerBasesList<MySerializer>(MyPolymorphicClassesForRegistering{});
|
||||
//serialize our data
|
||||
MySerializer ser{OutputAdapter{buffer}, &ctx};
|
||||
ser.object(data);
|
||||
auto &w = AdapterAccess::getWriter(ser);
|
||||
w.flush();
|
||||
writtenSize = w.writtenBytesCount();
|
||||
|
||||
//make sure that pointer linking context is valid
|
||||
//this ensures that all non-owning pointers points to data that has been serialized,
|
||||
//so we can successfully reconstruct pointers after deserialization
|
||||
assert(std::get<0>(ctx).isValid());
|
||||
}
|
||||
SomeShapes res{};
|
||||
{
|
||||
TContext ctx{};
|
||||
std::get<1>(ctx).registerBasesList<MyDeserializer>(MyPolymorphicClassesForRegistering{});
|
||||
//serialize our data
|
||||
MyDeserializer des{InputAdapter{buffer.begin(), writtenSize}, &ctx};
|
||||
des.object(res);
|
||||
auto &r = AdapterAccess::getReader(des);
|
||||
//check if everything went find
|
||||
assert(r.error() == ReaderError::NoError && r.isCompletedSuccessfully());
|
||||
//also check for dangling pointers, after deserialization
|
||||
assert(std::get<0>(ctx).isValid());
|
||||
// clear shared state from pointer linking context,
|
||||
// it is only required if there are any pointers that manage shared state, e.g. std::shared_ptr
|
||||
assert(res.weakPtr.use_count() == 2);//one in sharedList and one in pointer linking context
|
||||
std::get<0>(ctx).clearSharedState();
|
||||
assert(res.weakPtr.use_count() == 1);
|
||||
}
|
||||
assertSameShapes(data, res);
|
||||
return 0;
|
||||
}
|
||||
@@ -20,7 +20,6 @@
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
|
||||
#ifndef BITSERY_ADAPTER_BUFFER_H
|
||||
#define BITSERY_ADAPTER_BUFFER_H
|
||||
|
||||
@@ -30,15 +29,21 @@
|
||||
namespace bitsery {
|
||||
|
||||
//base class that stores container iterators, and is required for session support (for reading sessions data)
|
||||
template <typename Buffer>
|
||||
template<typename Buffer>
|
||||
class BufferIterators {
|
||||
protected:
|
||||
using TIterator = typename traits::BufferAdapterTraits<Buffer>::TIterator;
|
||||
static constexpr bool isConstBuffer = std::is_const<Buffer>::value;
|
||||
using BuffNonConst = typename std::remove_const<Buffer>::type;
|
||||
|
||||
protected:
|
||||
|
||||
using TIterator = typename std::conditional<isConstBuffer,
|
||||
typename traits::BufferAdapterTraits<BuffNonConst>::TConstIterator,
|
||||
typename traits::BufferAdapterTraits<BuffNonConst>::TIterator>::type;
|
||||
static_assert(details::IsDefined<TIterator>::value,
|
||||
"Please define BufferAdapterTraits or include from <bitsery/traits/...>");
|
||||
BufferIterators(TIterator begin, TIterator end)
|
||||
:posIt{begin},
|
||||
endIt{end}
|
||||
{
|
||||
: posIt{begin},
|
||||
endIt{end} {
|
||||
}
|
||||
|
||||
friend details::SessionAccess;
|
||||
@@ -47,26 +52,25 @@ namespace bitsery {
|
||||
TIterator endIt;
|
||||
};
|
||||
|
||||
|
||||
template <typename Buffer>
|
||||
class InputBufferAdapter: public BufferIterators<Buffer> {
|
||||
template<typename Buffer>
|
||||
class InputBufferAdapter : public BufferIterators<Buffer> {
|
||||
public:
|
||||
|
||||
using TIterator = typename BufferIterators<Buffer>::TIterator;
|
||||
using TValue = typename traits::BufferAdapterTraits<Buffer>::TValue;
|
||||
static_assert(details::IsDefined<TValue>::value, "Please define BufferAdapterTraits or include from <bitsery/traits/...>");
|
||||
static_assert(traits::ContainerTraits<Buffer>::isContiguous, "BufferAdapter only works with contiguous containers");
|
||||
using TValue = typename traits::BufferAdapterTraits<typename std::remove_const<Buffer>::type>::TValue;
|
||||
static_assert(details::IsDefined<TValue>::value,
|
||||
"Please define BufferAdapterTraits or include from <bitsery/traits/...>");
|
||||
static_assert(traits::ContainerTraits<typename std::remove_const<Buffer>::type>::isContiguous,
|
||||
"BufferAdapter only works with contiguous containers");
|
||||
|
||||
InputBufferAdapter(TIterator begin, TIterator end): BufferIterators<Buffer>(begin, end)
|
||||
{
|
||||
InputBufferAdapter(TIterator begin, TIterator endIt)
|
||||
: BufferIterators<Buffer>(begin, endIt) {
|
||||
}
|
||||
|
||||
InputBufferAdapter(TIterator begin, size_t size)
|
||||
:InputBufferAdapter(begin, std::next(begin, size))
|
||||
{
|
||||
: BufferIterators<Buffer>(begin, std::next(begin, size)) {
|
||||
}
|
||||
|
||||
void read(TValue* data, size_t size) {
|
||||
void read(TValue *data, size_t size) {
|
||||
//for optimization
|
||||
auto tmp = this->posIt;
|
||||
this->posIt += size;
|
||||
@@ -76,7 +80,6 @@ namespace bitsery {
|
||||
this->posIt -= size;
|
||||
//set everything to zeros
|
||||
std::memset(data, 0, size);
|
||||
|
||||
if (error() == ReaderError::NoError)
|
||||
setError(ReaderError::DataOverflow);
|
||||
}
|
||||
@@ -102,6 +105,47 @@ namespace bitsery {
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Buffer>
|
||||
class UnsafeInputBufferAdapter : public BufferIterators<Buffer> {
|
||||
public:
|
||||
|
||||
using TIterator = typename BufferIterators<Buffer>::TIterator;
|
||||
using TValue = typename traits::BufferAdapterTraits<typename std::remove_const<Buffer>::type>::TValue;
|
||||
static_assert(details::IsDefined<TValue>::value,
|
||||
"Please define BufferAdapterTraits or include from <bitsery/traits/...>");
|
||||
static_assert(traits::ContainerTraits<typename std::remove_const<Buffer>::type>::isContiguous,
|
||||
"BufferAdapter only works with contiguous containers");
|
||||
|
||||
UnsafeInputBufferAdapter(TIterator beginIt, TIterator endIt) : BufferIterators<Buffer>(beginIt, endIt) {
|
||||
}
|
||||
|
||||
UnsafeInputBufferAdapter(TIterator begin, size_t size)
|
||||
: BufferIterators<Buffer>(begin, std::next(begin, size)) {
|
||||
}
|
||||
|
||||
void read(TValue *data, size_t size) {
|
||||
//for optimization
|
||||
auto tmp = this->posIt;
|
||||
this->posIt += size;
|
||||
assert(std::distance(this->posIt, this->endIt) >= 0);
|
||||
std::memcpy(data, std::addressof(*tmp), size);
|
||||
}
|
||||
|
||||
ReaderError error() const {
|
||||
return err;
|
||||
}
|
||||
|
||||
void setError(ReaderError error) {
|
||||
err = error;
|
||||
}
|
||||
|
||||
bool isCompletedSuccessfully() const {
|
||||
return this->posIt == this->endIt;
|
||||
}
|
||||
|
||||
private:
|
||||
ReaderError err = ReaderError::NoError;
|
||||
};
|
||||
|
||||
template<typename Buffer>
|
||||
class OutputBufferAdapter {
|
||||
@@ -110,17 +154,17 @@ namespace bitsery {
|
||||
using TIterator = typename traits::BufferAdapterTraits<Buffer>::TIterator;
|
||||
using TValue = typename traits::BufferAdapterTraits<Buffer>::TValue;
|
||||
|
||||
static_assert(details::IsDefined<TValue>::value, "Please define BufferAdapterTraits or include from <bitsery/traits/...>");
|
||||
static_assert(traits::ContainerTraits<Buffer>::isContiguous, "BufferAdapter only works with contiguous containers");
|
||||
static_assert(details::IsDefined<TValue>::value,
|
||||
"Please define BufferAdapterTraits or include from <bitsery/traits/...>");
|
||||
static_assert(traits::ContainerTraits<Buffer>::isContiguous,
|
||||
"BufferAdapter only works with contiguous containers");
|
||||
|
||||
OutputBufferAdapter(Buffer &buffer)
|
||||
: _buffer{std::addressof(buffer)}
|
||||
{
|
||||
: _buffer{std::addressof(buffer)} {
|
||||
|
||||
init(TResizable{});
|
||||
}
|
||||
|
||||
|
||||
void write(const TValue *data, size_t size) {
|
||||
writeInternal(data, size, TResizable{});
|
||||
}
|
||||
@@ -136,7 +180,7 @@ namespace bitsery {
|
||||
private:
|
||||
using TResizable = std::integral_constant<bool, traits::ContainerTraits<Buffer>::isResizable>;
|
||||
|
||||
Buffer* _buffer;
|
||||
Buffer *_buffer;
|
||||
TIterator _outIt{};
|
||||
TIterator _end{};
|
||||
|
||||
@@ -163,7 +207,7 @@ namespace bitsery {
|
||||
#else
|
||||
auto tmp = _outIt;
|
||||
_outIt += size;
|
||||
if (std::distance(_outIt , _end) >= 0) {
|
||||
if (std::distance(_outIt, _end) >= 0) {
|
||||
std::memcpy(std::addressof(*tmp), data, size);
|
||||
#endif
|
||||
} else {
|
||||
@@ -201,7 +245,6 @@ namespace bitsery {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif //BITSERY_ADAPTER_BUFFER_H
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
|
||||
#ifndef BITSERY_ADAPTER_STREAM_H
|
||||
#define BITSERY_ADAPTER_STREAM_H
|
||||
|
||||
@@ -28,7 +27,6 @@
|
||||
#include "../traits/array.h"
|
||||
#include <ios>
|
||||
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
template <typename TChar, typename CharTraits>
|
||||
|
||||
@@ -20,8 +20,6 @@
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
|
||||
|
||||
#ifndef BITSERY_ADAPTER_READER_H
|
||||
#define BITSERY_ADAPTER_READER_H
|
||||
|
||||
@@ -29,7 +27,6 @@
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
template <typename TReader>
|
||||
@@ -63,7 +60,6 @@ namespace bitsery {
|
||||
|
||||
~AdapterReader() noexcept = default;
|
||||
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void readBytes(T &v) {
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
@@ -115,17 +111,13 @@ namespace bitsery {
|
||||
}
|
||||
}
|
||||
|
||||
const InputAdapter& adapter() const {
|
||||
return _inputAdapter;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class AdapterReaderBitPackingWrapper<AdapterReader<InputAdapter, Config>>;
|
||||
|
||||
InputAdapter _inputAdapter;
|
||||
typename std::conditional<Config::BufferSessionsEnabled,
|
||||
session::SessionsReader<AdapterReader<InputAdapter, Config>>,
|
||||
session::DisabledSessionsReader<AdapterReader<InputAdapter, Config>>>::type
|
||||
session::DisabledSessionsReader<AdapterReader<InputAdapter, Config>>>::type
|
||||
_session;
|
||||
|
||||
template<typename T>
|
||||
@@ -201,7 +193,6 @@ namespace bitsery {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
void readBits(T &v, size_t bitsCount) {
|
||||
static_assert(std::is_integral<T>() && std::is_unsigned<T>(), "");
|
||||
@@ -249,7 +240,7 @@ namespace bitsery {
|
||||
auto bitsLeft = size;
|
||||
T res{};
|
||||
while (bitsLeft > 0) {
|
||||
auto bits = std::min(bitsLeft, details::BitsSize<UnsignedValue>::value);
|
||||
auto bits = (std::min)(bitsLeft, details::BitsSize<UnsignedValue>::value);
|
||||
if (m_scratchBits < bits) {
|
||||
UnsignedValue tmp;
|
||||
_reader.template readBytes<sizeof(UnsignedValue), UnsignedValue>(tmp);
|
||||
|
||||
@@ -20,8 +20,6 @@
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
|
||||
|
||||
#ifndef BITSERY_ADAPTER_WRITER_H
|
||||
#define BITSERY_ADAPTER_WRITER_H
|
||||
|
||||
@@ -30,7 +28,6 @@
|
||||
#include <cassert>
|
||||
#include <utility>
|
||||
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
template <typename Config>
|
||||
@@ -100,7 +97,6 @@ namespace bitsery {
|
||||
//helper type for default config
|
||||
using MeasureSize = BasicMeasureSize<DefaultConfig>;
|
||||
|
||||
|
||||
template <typename TWriter>
|
||||
class AdapterWriterBitPackingWrapper;
|
||||
|
||||
@@ -174,10 +170,6 @@ namespace bitsery {
|
||||
_session.end(*this);
|
||||
}
|
||||
|
||||
const OutputAdapter& adapter() const {
|
||||
return _outputAdapter;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class AdapterWriterBitPackingWrapper<AdapterWriter<OutputAdapter, Config>>;
|
||||
template<typename T>
|
||||
@@ -303,7 +295,7 @@ namespace bitsery {
|
||||
auto value = v;
|
||||
auto bitsLeft = size;
|
||||
while (bitsLeft > 0) {
|
||||
auto bits = std::min(bitsLeft, valueSize);
|
||||
auto bits = (std::min)(bitsLeft, valueSize);
|
||||
_scratch |= static_cast<ScratchType>( value ) << _scratchBits;
|
||||
_scratchBits += bits;
|
||||
if (_scratchBits >= valueSize) {
|
||||
@@ -332,7 +324,7 @@ namespace bitsery {
|
||||
}
|
||||
}
|
||||
|
||||
const UnsignedType _MASK = std::numeric_limits<UnsignedType>::max();
|
||||
const UnsignedType _MASK = (std::numeric_limits<UnsignedType>::max)();
|
||||
ScratchType _scratch{};
|
||||
size_t _scratchBits{};
|
||||
TWriter& _writer;
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
#define BITSERY_BITSERY_H
|
||||
|
||||
#define BITSERY_MAJOR_VERSION 4
|
||||
#define BITSERY_MINOR_VERSION 2
|
||||
#define BITSERY_MINOR_VERSION 5
|
||||
#define BITSERY_PATCH_VERSION 1
|
||||
|
||||
#define BITSERY_QUOTE_MACRO(name) #name
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include <vector>
|
||||
#include <stack>
|
||||
#include <cstring>
|
||||
#include <climits>
|
||||
#include "adapter_utils.h"
|
||||
#include "not_defined_type.h"
|
||||
|
||||
@@ -40,7 +41,7 @@ namespace bitsery {
|
||||
|
||||
template<typename T>
|
||||
struct BitsSize:public std::integral_constant<size_t, sizeof(T) * 8> {
|
||||
|
||||
static_assert(CHAR_BIT == 8, "only support systems with byte size of 8 bits");
|
||||
};
|
||||
|
||||
//add swap functions to class, to avoid compilation warning about unused functions
|
||||
|
||||
@@ -56,7 +56,7 @@ namespace bitsery {
|
||||
int& operator*() {
|
||||
return data;
|
||||
}
|
||||
int data;
|
||||
int data{};
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
|
||||
@@ -32,17 +32,29 @@
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
//this allows to call private serialize method for the class
|
||||
//just make friend it to that class
|
||||
struct Access {
|
||||
//this allows to call private serialize method, and construct instance (if no default constructor is provided) for your type
|
||||
//just make friend it in your class
|
||||
class Access {
|
||||
public:
|
||||
template<typename S, typename T>
|
||||
static auto serialize(S &s, T &obj) -> decltype(obj.serialize(s)) {
|
||||
obj.serialize(s);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static T create() {
|
||||
//if you get an error here, please create default constructor
|
||||
return T{};
|
||||
}
|
||||
template <typename T>
|
||||
static T* createInHeap() {
|
||||
return new T{};
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
//when call to serialize function is ambiguous (member and non-member serialize function exists for a type)
|
||||
//specialize this class, by inheriting from either UseNonMemberFnc or UseMemberFnc
|
||||
//specialize this class by inheriting from either UseNonMemberFnc or UseMemberFnc
|
||||
//e.g.
|
||||
//template <> struct SelectSerializeFnc<MyDerivedClass>:UseMemberFnc {};
|
||||
template<typename T>
|
||||
@@ -56,7 +68,7 @@ namespace bitsery {
|
||||
};
|
||||
|
||||
|
||||
//serializer/deserializer, does not public interface to get underlying writer/reader
|
||||
//serializer/deserializer, does not have public interface to get underlying writer/reader
|
||||
//to prevent users from using writer/reader directly, because they have different interface
|
||||
//and they cannot be used describing serialization flows.: use extensions for this reason.
|
||||
//this class allows to get underlying adapter writer/reader, and only should be used outside serialization functions.
|
||||
@@ -173,12 +185,12 @@ namespace bitsery {
|
||||
#endif
|
||||
|
||||
|
||||
//used for extensions, when extension TValue = void
|
||||
//used for extensions when extension TValue = void
|
||||
struct DummyType {
|
||||
};
|
||||
|
||||
/*
|
||||
* this includes all integral types floats and enums(except bool)
|
||||
* this includes all integral types, floats and enums(except bool)
|
||||
*/
|
||||
template<typename T>
|
||||
struct IsFundamentalType : std::integral_constant<bool,
|
||||
@@ -312,13 +324,13 @@ namespace bitsery {
|
||||
template<typename TCast, typename ... Args>
|
||||
TCast *getContextImpl(std::tuple<Args...> *ctx, std::true_type) {
|
||||
using TCastIndex = GetTypeIndex<TCast, Args...>;
|
||||
static_assert(HasType<TCast, Args...>::value, "Invalid context cast. Type doesn't exists.");
|
||||
static_assert(HasType<TCast, Args...>::value, "Invalid context cast. Context type doesn't exists.\nSome functionality requires (de)seserializer to have specific context.");
|
||||
return std::addressof(std::get<TCastIndex::value>(*ctx));
|
||||
}
|
||||
|
||||
template<typename TCast, typename TContext>
|
||||
TCast *getContextImpl(TContext *ctx, std::false_type) {
|
||||
static_assert(std::is_convertible<TContext *, TCast *>::value, "Invalid context cast. Type doesn't exists.");
|
||||
static_assert(std::is_convertible<TContext *, TCast *>::value, "Invalid context cast. Context type doesn't exists.\nSome functionality requires (de)seserializer to have specific context.");
|
||||
return static_cast<TCast *>(ctx);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,24 @@
|
||||
//MIT License
|
||||
//
|
||||
// Created by fraillt on 17.10.5.
|
||||
//Copyright (c) 2018 Mindaugas Vinkelis
|
||||
//
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
//of this software and associated documentation files (the "Software"), to deal
|
||||
//in the Software without restriction, including without limitation the rights
|
||||
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
//copies of the Software, and to permit persons to whom the Software is
|
||||
//furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//The above copyright notice and this permission notice shall be included in all
|
||||
//copies or substantial portions of the Software.
|
||||
//
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_DETAILS_SESSIONS_H
|
||||
#define BITSERY_DETAILS_SESSIONS_H
|
||||
|
||||
176
include/bitsery/ext/compact_value.h
Normal file
176
include/bitsery/ext/compact_value.h
Normal file
@@ -0,0 +1,176 @@
|
||||
//MIT License
|
||||
//
|
||||
//Copyright (c) 2018 Mindaugas Vinkelis
|
||||
//
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
//of this software and associated documentation files (the "Software"), to deal
|
||||
//in the Software without restriction, including without limitation the rights
|
||||
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
//copies of the Software, and to permit persons to whom the Software is
|
||||
//furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//The above copyright notice and this permission notice shall be included in all
|
||||
//copies or substantial portions of the Software.
|
||||
//
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_EXT_COMPACT_VALUE_H
|
||||
#define BITSERY_EXT_COMPACT_VALUE_H
|
||||
|
||||
#include "../details/serialization_common.h"
|
||||
#include "../details/adapter_common.h"
|
||||
#include <cassert>
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
namespace details {
|
||||
|
||||
template <bool CheckOverflow>
|
||||
class CompactValueImpl {
|
||||
public:
|
||||
|
||||
template<typename Ser, typename Writer, typename T, typename Fnc>
|
||||
void serialize(Ser &s, Writer &writer, const T &v, Fnc &&) const {
|
||||
static_assert(std::is_integral<T>::value || std::is_enum<T>::value, "");
|
||||
using TValue = typename IntegralFromFundamental<T>::TValue;
|
||||
serializeImpl(s, writer, reinterpret_cast<const TValue&>(v), std::integral_constant<bool, sizeof(T) != 1>{});
|
||||
}
|
||||
|
||||
template<typename Des, typename Reader, typename T, typename Fnc>
|
||||
void deserialize(Des &d, Reader &reader, T &v, Fnc &&) const {
|
||||
static_assert(std::is_integral<T>::value || std::is_enum<T>::value, "");
|
||||
using TValue = typename IntegralFromFundamental<T>::TValue;
|
||||
deserializeImpl(d, reader, reinterpret_cast<TValue &>(v), std::integral_constant<bool, sizeof(T) != 1>{});
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
// if value is 1byte size, just serialize/ deserialize whole value
|
||||
template<typename Ser, typename Writer, typename T>
|
||||
void serializeImpl(Ser &s, Writer &, const T &v, std::false_type) const {
|
||||
s.value1b(v);
|
||||
}
|
||||
|
||||
template<typename Des, typename Reader, typename T>
|
||||
void deserializeImpl(Des &d, Reader &, T &v, std::false_type) const {
|
||||
d.value1b(v);
|
||||
}
|
||||
|
||||
// when value is bigger than 1byte size,
|
||||
template<typename Ser, typename Writer, typename T>
|
||||
void serializeImpl(Ser &, Writer &writer, const T &v, std::true_type) const {
|
||||
auto val = zigZagEncode(v, std::is_signed<typename IntegralFromFundamental<T>::TValue>{});
|
||||
writeBytes(writer, val);
|
||||
}
|
||||
|
||||
|
||||
template<typename Des, typename Reader, typename T>
|
||||
void deserializeImpl(Des &, Reader &reader, T &v, std::true_type) const {
|
||||
using TUnsigned = SameSizeUnsigned<T>;
|
||||
TUnsigned res{};
|
||||
readBytes(reader, res);
|
||||
v = zigZagDecode<T>(res, std::is_signed<typename IntegralFromFundamental<T>::TValue>{});
|
||||
}
|
||||
|
||||
// zigzag encode signed types
|
||||
template<typename T>
|
||||
const SameSizeUnsigned<T> &zigZagEncode(const T &v, std::false_type) const {
|
||||
return v;
|
||||
}
|
||||
|
||||
template<typename TResult, typename TUnsigned>
|
||||
const TResult &zigZagDecode(const TUnsigned &v, std::false_type) const{
|
||||
return v;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
SameSizeUnsigned<T> zigZagEncode(const T &v, std::true_type) const {
|
||||
return (v << 1) ^ (v >> (BitsSize<T>::value - 1));
|
||||
}
|
||||
|
||||
template<typename TResult, typename TUnsigned>
|
||||
TResult zigZagDecode(TUnsigned v, std::true_type) const {
|
||||
return (v >> 1) ^ (~(v & 1) + 1); // same as -(v & 1), but no warning on VisualStudio
|
||||
}
|
||||
|
||||
// write/read bytes one by one
|
||||
template<typename Writer, typename T>
|
||||
void writeBytes(Writer &w, const T &v) const {
|
||||
auto val = v;
|
||||
while(val > 0x7Fu) {
|
||||
w.template writeBytes<1>(static_cast<uint8_t>(val | 0x80u));
|
||||
val >>=7u;
|
||||
}
|
||||
w.template writeBytes<1>(static_cast<uint8_t>(val));
|
||||
}
|
||||
|
||||
template<typename Reader, typename T>
|
||||
void readBytes(Reader &r, T &v) const {
|
||||
constexpr auto TBITS = sizeof(T)*8;
|
||||
uint8_t b1{0x80u};
|
||||
auto i = 0u;
|
||||
for (;i < TBITS && b1 > 0x7Fu; i +=7u) {
|
||||
r.template readBytes<1>(b1);
|
||||
v += static_cast<T>(b1 & 0x7Fu) << i;
|
||||
}
|
||||
checkReadOverflow<Reader, T>(r, i, b1, std::integral_constant<bool, CheckOverflow>{});
|
||||
}
|
||||
template <typename Reader, typename T>
|
||||
void checkReadOverflow(Reader &r, unsigned shiftedBy, uint8_t remainder, std::true_type) const {
|
||||
constexpr auto TBITS = sizeof(T)*8;
|
||||
if (shiftedBy > TBITS && remainder >> (TBITS + 7 - shiftedBy)) {
|
||||
r.setError(bitsery::ReaderError::DataOverflow);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Reader, typename T>
|
||||
void checkReadOverflow(Reader &, unsigned , uint8_t , std::false_type) const {
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace ext {
|
||||
|
||||
// this type will use value overload, and do not check if type is sufficiently large during deserialization
|
||||
class CompactValue: public details::CompactValueImpl<false> {};
|
||||
|
||||
// this type will enable object overload, and set DataOverflow if value doesn't fit in type, during deserialization
|
||||
class CompactValueAsObject: public details::CompactValueImpl<true> {};
|
||||
|
||||
}
|
||||
|
||||
namespace traits {
|
||||
template<typename T>
|
||||
struct ExtensionTraits<ext::CompactValue, T> {
|
||||
using TValue = T;
|
||||
static constexpr bool SupportValueOverload = true;
|
||||
// disable object overload, because we don't have implemented serialization function for fundamental types
|
||||
static constexpr bool SupportObjectOverload = false;
|
||||
static constexpr bool SupportLambdaOverload = false;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct ExtensionTraits<ext::CompactValueAsObject, T> {
|
||||
// use dummy implemenations for value and object overload
|
||||
using TValue = void;
|
||||
// only enable object overload
|
||||
static constexpr bool SupportValueOverload = false;
|
||||
static constexpr bool SupportObjectOverload = true;
|
||||
static constexpr bool SupportLambdaOverload = false;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif //BITSERY_EXT_COMPACT_VALUE_H
|
||||
@@ -23,150 +23,161 @@
|
||||
#ifndef BITSERY_EXT_POINTER_H
|
||||
#define BITSERY_EXT_POINTER_H
|
||||
|
||||
#include <cassert>
|
||||
#include "../traits/core/traits.h"
|
||||
#include "utils/pointer_utils.h"
|
||||
#include "utils/polymorphism_utils.h"
|
||||
#include "utils/rtti_utils.h"
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
namespace ext {
|
||||
|
||||
namespace details_pointer {
|
||||
namespace pointer_details {
|
||||
|
||||
template<typename S>
|
||||
PointerLinkingContext &getLinkingContext(S &s) {
|
||||
auto res = s.template context<PointerLinkingContext>();
|
||||
assert(res != nullptr);
|
||||
return *res;
|
||||
}
|
||||
template<typename T>
|
||||
struct PtrOwnerManager {
|
||||
static_assert(std::is_pointer<T>::value, "");
|
||||
|
||||
using TElement = typename std::remove_pointer<T>::type;
|
||||
|
||||
template<typename TObject>
|
||||
struct RawPointerObjectHandler {
|
||||
|
||||
using TPointer = TObject;
|
||||
|
||||
template<typename T>
|
||||
void create(TObject &obj) const {
|
||||
obj = new T{};
|
||||
static TElement* getPtr(T &obj) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
void destroy(TObject &obj) const {
|
||||
static constexpr PointerOwnershipType getOwnership() {
|
||||
return PointerOwnershipType::Owner;
|
||||
}
|
||||
|
||||
static void assign(T& obj, TElement* valuePtr) {
|
||||
delete obj;
|
||||
obj = valuePtr;
|
||||
}
|
||||
|
||||
static void clear(T& obj) {
|
||||
delete obj;
|
||||
obj = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
const TPointer getPtr(const TObject &obj) const {
|
||||
template<typename T>
|
||||
struct PtrObserverManager {
|
||||
static_assert(std::is_pointer<T>::value, "");
|
||||
|
||||
using TElement = typename std::remove_pointer<T>::type;
|
||||
|
||||
//observer must return reference to pointer, so that it could be updated later
|
||||
static TElement*& getPtrRef(T& obj) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
TPointer getPtr(TObject &obj) const {
|
||||
static TElement* getPtr(T& obj) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
static constexpr PointerOwnershipType getOwnership() {
|
||||
return PointerOwnershipType::Observer;
|
||||
}
|
||||
|
||||
static void assign(T& obj, TElement* valuePtr) {
|
||||
//do not delete existing object
|
||||
obj = valuePtr;
|
||||
}
|
||||
|
||||
static void clear(T& obj) {
|
||||
obj = nullptr;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template <typename TObject>
|
||||
struct RawPointerManagerConfig {
|
||||
using RTTI = bitsery::ext::utils::StandardRTTI;
|
||||
static constexpr PointerOwnershipType OwnershipType = PointerOwnershipType::Owner;
|
||||
template<typename T>
|
||||
struct NonPtrManager {
|
||||
|
||||
using Handler = RawPointerObjectHandler<TObject>;
|
||||
static_assert(!std::is_pointer<T>::value, "");
|
||||
|
||||
static std::unique_ptr<utils::PointerSharedContextBase> createSharedContext(TObject &) {
|
||||
return {};
|
||||
using TElement = T;
|
||||
|
||||
static TElement* getPtr(T& obj) {
|
||||
return &obj;
|
||||
}
|
||||
|
||||
static void restoreFromSharedContext(TObject &, utils::PointerSharedContextBase *) {
|
||||
static constexpr PointerOwnershipType getOwnership() {
|
||||
return PointerOwnershipType::Owner;
|
||||
}
|
||||
|
||||
// this code is unreachable for reference type, but is necessary to compile
|
||||
// LCOV_EXCL_START
|
||||
static void assign(T& , TElement* ) {}
|
||||
|
||||
static void clear(T& ) {}
|
||||
// LCOV_EXCL_STOP
|
||||
|
||||
};
|
||||
|
||||
// this class is used by NonPtrManager
|
||||
struct NoRTTI {
|
||||
template<typename TBase>
|
||||
static size_t get(TBase& ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<typename TBase>
|
||||
static constexpr size_t get() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<typename TBase, typename TDerived>
|
||||
static constexpr TDerived* cast(TBase* obj) {
|
||||
static_assert(!std::is_pointer<TDerived>::value, "");
|
||||
return dynamic_cast<TDerived*>(obj);
|
||||
}
|
||||
|
||||
template<typename TBase>
|
||||
static constexpr bool isPolymorphic() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
class PointerOwner : public utils::PointerOwnerManager<details_pointer::RawPointerManagerConfig> {
|
||||
template<typename RTTI>
|
||||
using PointerOwnerBase = pointer_utils::PointerObjectExtensionBase<
|
||||
pointer_details::PtrOwnerManager, PolymorphicContext, RTTI>;
|
||||
|
||||
using PointerOwner = PointerOwnerBase<StandardRTTI>;
|
||||
|
||||
using PointerObserver = pointer_utils::PointerObjectExtensionBase<
|
||||
pointer_details::PtrObserverManager, PolymorphicContext, pointer_details::NoRTTI>;
|
||||
|
||||
//inherit from PointerObjectExtensionBase in order to specify PointerType::NotNull
|
||||
class ReferencedByPointer : public pointer_utils::PointerObjectExtensionBase<
|
||||
pointer_details::NonPtrManager, PolymorphicContext, pointer_details::NoRTTI> {
|
||||
public:
|
||||
explicit PointerOwner(PointerType ptrType = PointerType::Nullable) : PointerOwnerManager(ptrType) {}
|
||||
};
|
||||
|
||||
class PointerObserver {
|
||||
public:
|
||||
|
||||
explicit PointerObserver(PointerType ptrType = PointerType::Nullable) : _ptrType{ptrType} {}
|
||||
|
||||
template<typename Ser, typename Writer, typename T, typename Fnc>
|
||||
void serialize(Ser &ser, Writer &w, const T &obj, Fnc &&) const {
|
||||
auto &ctx = details_pointer::getLinkingContext(ser);
|
||||
if (obj) {
|
||||
details::writeSize(w, ctx.getInfoByPtr(obj, PointerOwnershipType::Observer).id);
|
||||
} else {
|
||||
assert(_ptrType == PointerType::Nullable);
|
||||
details::writeSize(w, 0);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Des, typename Reader, typename T, typename Fnc>
|
||||
void deserialize(Des &des, Reader &r, T &obj, Fnc &&) const {
|
||||
size_t id{};
|
||||
details::readSize(r, id, std::numeric_limits<size_t>::max());
|
||||
if (id) {
|
||||
auto &ctx = details_pointer::getLinkingContext(des);
|
||||
ctx.getInfoById(id, PointerOwnershipType::Observer).processObserver(reinterpret_cast<void *&>(obj));
|
||||
} else {
|
||||
if (_ptrType == PointerType::Nullable)
|
||||
obj = nullptr;
|
||||
else
|
||||
r.setError(ReaderError::InvalidPointer);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
PointerType _ptrType;
|
||||
};
|
||||
|
||||
class ReferencedByPointer {
|
||||
public:
|
||||
template<typename Ser, typename Writer, typename T, typename Fnc>
|
||||
void serialize(Ser &ser, Writer &w, const T &obj, Fnc &&fnc) const {
|
||||
auto &ctx = details_pointer::getLinkingContext(ser);
|
||||
details::writeSize(w, ctx.getInfoByPtr(&obj, PointerOwnershipType::Owner).id);
|
||||
fnc(const_cast<T &>(obj));
|
||||
}
|
||||
|
||||
template<typename Des, typename Reader, typename T, typename Fnc>
|
||||
void deserialize(Des &des, Reader &r, T &obj, Fnc &&fnc) const {
|
||||
size_t id{};
|
||||
details::readSize(r, id, std::numeric_limits<size_t>::max());
|
||||
if (id) {
|
||||
auto &ctx = details_pointer::getLinkingContext(des);
|
||||
fnc(obj);
|
||||
ctx.getInfoById(id, PointerOwnershipType::Owner).processOwner(&obj);
|
||||
} else {
|
||||
//cannot be null for references
|
||||
r.setError(ReaderError::InvalidPointer);
|
||||
}
|
||||
}
|
||||
ReferencedByPointer() : pointer_utils::PointerObjectExtensionBase<
|
||||
pointer_details::NonPtrManager, PolymorphicContext, pointer_details::NoRTTI>(
|
||||
PointerType::NotNull) {}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace traits {
|
||||
|
||||
template<typename T>
|
||||
struct ExtensionTraits<ext::PointerOwner, T *> {
|
||||
template<typename T, typename RTTI>
|
||||
struct ExtensionTraits<ext::PointerOwnerBase<RTTI>, T*> {
|
||||
using TValue = T;
|
||||
static constexpr bool SupportValueOverload = true;
|
||||
static constexpr bool SupportObjectOverload = true;
|
||||
//pointers cannot have lamba overload, when polymorphism support will be added
|
||||
static constexpr bool SupportLambdaOverload = false;
|
||||
//if underlying type is not polymorphic, then we can enable lambda syntax
|
||||
static constexpr bool SupportLambdaOverload = !RTTI::template isPolymorphic<TValue>();
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct ExtensionTraits<ext::PointerObserver, T *> {
|
||||
struct ExtensionTraits<ext::PointerObserver, T*> {
|
||||
//although pointer observer doesn't serialize anything, but we still add value overload support to be consistent with pointer owners
|
||||
//observer only writes/reads pointer id from pointer linking context
|
||||
using TValue = T;
|
||||
static constexpr bool SupportValueOverload = true;
|
||||
static constexpr bool SupportObjectOverload = true;
|
||||
//pointers cannot have lamba overload, when polymorphism support will be added
|
||||
static constexpr bool SupportLambdaOverload = false;
|
||||
};
|
||||
|
||||
@@ -182,5 +193,4 @@ namespace bitsery {
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif //BITSERY_EXT_POINTER_H
|
||||
|
||||
@@ -25,6 +25,9 @@
|
||||
|
||||
#include "../traits/core/traits.h"
|
||||
#include "../details/adapter_utils.h"
|
||||
#include "../details/serialization_common.h"
|
||||
//we need this, so we could reserve for non ordered map
|
||||
#include <unordered_map>
|
||||
|
||||
namespace bitsery {
|
||||
namespace ext {
|
||||
@@ -53,17 +56,30 @@ namespace bitsery {
|
||||
|
||||
size_t size{};
|
||||
details::readSize(reader, size, _maxSize);
|
||||
auto hint = obj.begin();
|
||||
obj.clear();
|
||||
reserve(obj, size);
|
||||
|
||||
auto hint = obj.begin();
|
||||
for (auto i = 0u; i < size; ++i) {
|
||||
TKey key;
|
||||
TValue value;
|
||||
auto key{bitsery::Access::create<TKey>()};
|
||||
auto value{bitsery::Access::create<TValue>()};
|
||||
fnc(key, value);
|
||||
hint = obj.emplace_hint(hint, std::move(key), std::move(value));
|
||||
}
|
||||
}
|
||||
private:
|
||||
template <typename Key, typename T, typename Hash, typename KeyEqual, typename Allocator>
|
||||
void reserve(std::unordered_map<Key, T, Hash, KeyEqual, Allocator>& obj, size_t size) const {
|
||||
obj.reserve(size);
|
||||
}
|
||||
template <typename Key, typename T, typename Hash, typename KeyEqual, typename Allocator>
|
||||
void reserve(std::unordered_multimap<Key, T, Hash, KeyEqual, Allocator>& obj, size_t size) const {
|
||||
obj.reserve(size);
|
||||
}
|
||||
template <typename T>
|
||||
void reserve(T& , size_t ) const {
|
||||
//for ordered container do nothing
|
||||
}
|
||||
size_t _maxSize;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
//}
|
||||
#include <type_traits>
|
||||
#include "../traits/core/traits.h"
|
||||
#include "../details/serialization_common.h"
|
||||
|
||||
namespace bitsery {
|
||||
namespace ext {
|
||||
@@ -54,8 +55,7 @@ namespace bitsery {
|
||||
using TOpt = typename std::remove_cv<T>::type;
|
||||
using TVal = typename TOpt::value_type;
|
||||
static_assert(std::is_same<TOpt, std_optional<TVal>>(), "");
|
||||
static_assert(std::is_default_constructible<TVal>::value, "");
|
||||
};
|
||||
}
|
||||
|
||||
template<typename Ser, typename Writer, typename T, typename Fnc>
|
||||
void serialize(Ser &ser, Writer &, const T &obj, Fnc &&fnc) const {
|
||||
@@ -75,7 +75,7 @@ namespace bitsery {
|
||||
if (_alignBeforeData)
|
||||
des.align();
|
||||
if (exists) {
|
||||
typename T::value_type tmp{};
|
||||
auto tmp{::bitsery::Access::create<typename T::value_type>()};
|
||||
fnc(tmp);
|
||||
obj = tmp;
|
||||
} else {
|
||||
|
||||
@@ -51,15 +51,15 @@ namespace bitsery {
|
||||
}
|
||||
};
|
||||
//inherit from queue so we could take underlying container
|
||||
template <typename T, typename C>
|
||||
struct PriorityQueueCnt : public std::priority_queue<T, C>
|
||||
template <typename T, typename Seq, typename Cmp>
|
||||
struct PriorityQueueCnt : public std::priority_queue<T, Seq, Cmp>
|
||||
{
|
||||
static const C& getContainer(const std::priority_queue<T, C>& s )
|
||||
static const Seq& getContainer(const std::priority_queue<T, Seq, Cmp>& s )
|
||||
{
|
||||
//get address of underlying container
|
||||
return s.*(&PriorityQueueCnt::c);
|
||||
}
|
||||
static C& getContainer(std::priority_queue<T, C>& s )
|
||||
static Seq& getContainer(std::priority_queue<T, Seq, Cmp>& s )
|
||||
{
|
||||
//get address of underlying container
|
||||
return s.*(&PriorityQueueCnt::c);
|
||||
@@ -82,14 +82,14 @@ namespace bitsery {
|
||||
}
|
||||
|
||||
//for priority_queue
|
||||
template<typename Ser, typename Writer, typename T, typename C, typename Fnc>
|
||||
void serialize(Ser &ser, Writer &, const std::priority_queue<T,C> &obj, Fnc &&fnc) const {
|
||||
ser.container(PriorityQueueCnt<T,C>::getContainer(obj), _maxSize, std::forward<Fnc>(fnc));
|
||||
template<typename Ser, typename Writer, typename T, typename C, typename Comp, typename Fnc>
|
||||
void serialize(Ser &ser, Writer &, const std::priority_queue<T,C, Comp> &obj, Fnc &&fnc) const {
|
||||
ser.container(PriorityQueueCnt<T,C, Comp>::getContainer(obj), _maxSize, std::forward<Fnc>(fnc));
|
||||
}
|
||||
|
||||
template<typename Des, typename Reader, typename T, typename C, typename Fnc>
|
||||
void deserialize(Des &des, Reader &, std::priority_queue<T,C> &obj, Fnc &&fnc) const {
|
||||
des.container(PriorityQueueCnt<T,C>::getContainer(obj), _maxSize, std::forward<Fnc>(fnc));
|
||||
template<typename Des, typename Reader, typename T, typename C, typename Comp, typename Fnc>
|
||||
void deserialize(Des &des, Reader &, std::priority_queue<T,C, Comp> &obj, Fnc &&fnc) const {
|
||||
des.container(PriorityQueueCnt<T,C, Comp>::getContainer(obj), _maxSize, std::forward<Fnc>(fnc));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@@ -25,9 +25,9 @@
|
||||
|
||||
#include <cassert>
|
||||
#include "../details/adapter_utils.h"
|
||||
//we need this, so we could
|
||||
#include "../details/serialization_common.h"
|
||||
//we need this, so we could reserve for non ordered set
|
||||
#include <unordered_set>
|
||||
#include "../traits/core/traits.h"
|
||||
|
||||
namespace bitsery {
|
||||
namespace ext {
|
||||
@@ -54,26 +54,28 @@ namespace bitsery {
|
||||
|
||||
size_t size{};
|
||||
details::readSize(reader, size, _maxSize);
|
||||
auto hint = obj.begin();
|
||||
obj.clear();
|
||||
reserve(obj, size);
|
||||
|
||||
auto hint = obj.begin();
|
||||
for (auto i = 0u; i < size; ++i) {
|
||||
TKey key;
|
||||
auto key{bitsery::Access::create<TKey>()};
|
||||
fnc(key);
|
||||
hint = obj.emplace_hint(hint, std::move(key));
|
||||
}
|
||||
}
|
||||
private:
|
||||
|
||||
template <typename T>
|
||||
void reserve(std::unordered_set<T>& obj, size_t size) const {
|
||||
template <typename Key, typename Hash, typename KeyEqual, typename Allocator>
|
||||
void reserve(std::unordered_set<Key, Hash, KeyEqual, Allocator>& obj, size_t size) const {
|
||||
obj.reserve(size);
|
||||
}
|
||||
template <typename T>
|
||||
void reserve(std::unordered_multiset<T>& obj, size_t size) const {
|
||||
template <typename Key, typename Hash, typename KeyEqual, typename Allocator>
|
||||
void reserve(std::unordered_multiset<Key, Hash, KeyEqual, Allocator>& obj, size_t size) const {
|
||||
obj.reserve(size);
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <typename T>
|
||||
void reserve(T& , size_t ) const {
|
||||
//for ordered container do nothing
|
||||
|
||||
129
include/bitsery/ext/std_smart_ptr.h
Normal file
129
include/bitsery/ext/std_smart_ptr.h
Normal file
@@ -0,0 +1,129 @@
|
||||
//MIT License
|
||||
//
|
||||
//Copyright (c) 2018 Mindaugas Vinkelis
|
||||
//
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
//of this software and associated documentation files (the "Software"), to deal
|
||||
//in the Software without restriction, including without limitation the rights
|
||||
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
//copies of the Software, and to permit persons to whom the Software is
|
||||
//furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//The above copyright notice and this permission notice shall be included in all
|
||||
//copies or substantial portions of the Software.
|
||||
//
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_EXT_STD_SMART_PTR_H
|
||||
#define BITSERY_EXT_STD_SMART_PTR_H
|
||||
|
||||
#include <cassert>
|
||||
#include "../traits/core/traits.h"
|
||||
#include "utils/pointer_utils.h"
|
||||
#include "utils/polymorphism_utils.h"
|
||||
#include "utils/rtti_utils.h"
|
||||
#include <memory>
|
||||
|
||||
namespace bitsery {
|
||||
namespace ext {
|
||||
|
||||
namespace smart_ptr_details {
|
||||
|
||||
//further code is for managing shared ownership
|
||||
//do not nest this type in pointer manager class itself, because it will be different type for different T
|
||||
struct SharedPtrSharedState : pointer_utils::PointerSharedStateBase {
|
||||
std::shared_ptr<void> obj{};
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct SmartPtrOwnerManager {
|
||||
|
||||
using TElement = typename T::element_type;
|
||||
|
||||
template <typename TDeleter>
|
||||
static TElement *getPtr(std::unique_ptr<TElement, TDeleter> &obj) {
|
||||
return obj.get();
|
||||
}
|
||||
|
||||
static TElement *getPtr(std::shared_ptr<TElement> &obj) {
|
||||
return obj.get();
|
||||
}
|
||||
|
||||
static TElement *getPtr(std::weak_ptr<TElement> &obj) {
|
||||
if (auto ptr = obj.lock())
|
||||
return ptr.get();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static constexpr PointerOwnershipType getOwnership() {
|
||||
return ::bitsery::details::IsSpecializationOf<T, std::unique_ptr>::value
|
||||
? PointerOwnershipType::Owner
|
||||
: std::is_same<std::shared_ptr<TElement>, T>::value
|
||||
? PointerOwnershipType::SharedOwner
|
||||
: PointerOwnershipType::SharedObserver;
|
||||
}
|
||||
|
||||
static void clear(T &obj) {
|
||||
obj.reset();
|
||||
}
|
||||
|
||||
static void assign(T &obj, TElement *valuePtr) {
|
||||
obj.reset(valuePtr);
|
||||
}
|
||||
|
||||
//this is used, when old object exists and is the same type
|
||||
static std::unique_ptr<pointer_utils::PointerSharedStateBase> saveToSharedState(T &obj) {
|
||||
auto state = new SharedPtrSharedState{};
|
||||
//to work with weak_ptr and shared_ptr create new std::shared_ptr
|
||||
state->obj = std::shared_ptr<TElement>(obj);
|
||||
return std::unique_ptr<pointer_utils::PointerSharedStateBase>{state};
|
||||
}
|
||||
|
||||
//this is used, when old object doesn't exists or is not the same type
|
||||
static std::unique_ptr<pointer_utils::PointerSharedStateBase> createSharedState(TElement *valuePtr) {
|
||||
auto state = new SharedPtrSharedState{};
|
||||
state->obj = std::shared_ptr<TElement>(valuePtr);
|
||||
return std::unique_ptr<pointer_utils::PointerSharedStateBase>{state};
|
||||
}
|
||||
|
||||
static void loadFromSharedState(pointer_utils::PointerSharedStateBase *ctx, T &obj) {
|
||||
auto state = dynamic_cast<SharedPtrSharedState *>(ctx);
|
||||
//reinterpret_pointer_cast is only since c++17
|
||||
auto p = reinterpret_cast<TElement *>(state->obj.get());
|
||||
obj = std::shared_ptr<TElement>(state->obj, p);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
template<typename RTTI>
|
||||
using StdSmartPtrBase = pointer_utils::PointerObjectExtensionBase<
|
||||
smart_ptr_details::SmartPtrOwnerManager, PolymorphicContext, RTTI>;
|
||||
|
||||
//helper type for convienience
|
||||
using StdSmartPtr = StdSmartPtrBase<StandardRTTI>;
|
||||
|
||||
}
|
||||
|
||||
namespace traits {
|
||||
|
||||
template<typename T, typename RTTI>
|
||||
struct ExtensionTraits<ext::StdSmartPtrBase<RTTI>, T> {
|
||||
using TValue = typename T::element_type;
|
||||
static constexpr bool SupportValueOverload = true;
|
||||
static constexpr bool SupportObjectOverload = true;
|
||||
//if underlying type is not polymorphic, then we can enable lambda syntax
|
||||
static constexpr bool SupportLambdaOverload = !RTTI::template isPolymorphic<TValue>();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif //BITSERY_EXT_STD_SMART_PTR_H
|
||||
@@ -67,9 +67,9 @@ namespace bitsery {
|
||||
}
|
||||
|
||||
namespace traits {
|
||||
template<typename T>
|
||||
struct ExtensionTraits<ext::StdStack, T> {
|
||||
using TValue = typename T::value_type;
|
||||
template<typename T, typename Seq>
|
||||
struct ExtensionTraits<ext::StdStack, std::stack<T, Seq>> {
|
||||
using TValue = T;
|
||||
static constexpr bool SupportValueOverload = true;
|
||||
static constexpr bool SupportObjectOverload = true;
|
||||
static constexpr bool SupportLambdaOverload = true;
|
||||
|
||||
@@ -1,6 +1,24 @@
|
||||
//MIT License
|
||||
//
|
||||
// Created by fraillt on 17.11.30.
|
||||
//Copyright (c) 2018 Mindaugas Vinkelis
|
||||
//
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
//of this software and associated documentation files (the "Software"), to deal
|
||||
//in the Software without restriction, including without limitation the rights
|
||||
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
//copies of the Software, and to permit persons to whom the Software is
|
||||
//furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//The above copyright notice and this permission notice shall be included in all
|
||||
//copies or substantial portions of the Software.
|
||||
//
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_POINTER_UTILS_H
|
||||
#define BITSERY_POINTER_UTILS_H
|
||||
@@ -8,31 +26,110 @@
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
#include "polymorphism_utils.h"
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include "../../details/adapter_utils.h"
|
||||
#include "../../details/serialization_common.h"
|
||||
|
||||
namespace bitsery {
|
||||
namespace ext {
|
||||
|
||||
//change name
|
||||
enum class PointerType {
|
||||
Nullable,
|
||||
NotNull
|
||||
};
|
||||
|
||||
// Observer - not responsible for pointer lifetime management.
|
||||
// Owner - only ONE owner is responsible for this pointers creation/destruction
|
||||
// SharedOwner, SharedObserver - MANY shared owners is responsible for pointer creation/destruction
|
||||
// requires additional context to manage shared owners themselves.
|
||||
// SharedOwner actually manages life time e.g. std::shared_ptr
|
||||
// SharedObserver do not manage life time of the pointer, but can observe shared state .e.. std::weak_ptr
|
||||
// and differently from Observer, creates new object if necessary and saves to shared state
|
||||
enum class PointerOwnershipType : uint8_t {
|
||||
//is not responsible for pointer lifetime management.
|
||||
Observer,
|
||||
//only ONE owner is responsible for this pointers creation/destruction
|
||||
Owner,
|
||||
//MANY shared owners is responsible for pointer creation/destruction
|
||||
//requires additional context to manage shared owners themselves.
|
||||
Shared
|
||||
Observer,
|
||||
Owner,
|
||||
SharedOwner,
|
||||
SharedObserver
|
||||
};
|
||||
|
||||
//forward declaration
|
||||
class PointerLinkingContext;
|
||||
|
||||
namespace utils {
|
||||
namespace pointer_utils {
|
||||
|
||||
//this class is used to store context for shared ptr owners
|
||||
struct PointerSharedStateBase {
|
||||
virtual ~PointerSharedStateBase() = default;
|
||||
};
|
||||
|
||||
//PLC info is internal classes for serializer, and deserializer
|
||||
struct PLCInfo {
|
||||
explicit PLCInfo(PointerOwnershipType ownershipType_)
|
||||
: ownershipType{ownershipType_},
|
||||
isSharedProcessed{false} {};
|
||||
PointerOwnershipType ownershipType;
|
||||
bool isSharedProcessed;
|
||||
|
||||
void update(PointerOwnershipType ptrType) {
|
||||
//do nothing for observer
|
||||
if (ptrType == PointerOwnershipType::Observer)
|
||||
return;
|
||||
if (ownershipType == PointerOwnershipType::Observer) {
|
||||
//set ownership type
|
||||
ownershipType = ptrType;
|
||||
return;
|
||||
}
|
||||
//only shared ownership can get here multiple times
|
||||
assert(ptrType == PointerOwnershipType::SharedOwner || ptrType == PointerOwnershipType::SharedObserver);
|
||||
//check if need to update to SharedOwner
|
||||
if (ptrType == PointerOwnershipType::SharedOwner)
|
||||
ownershipType = ptrType;
|
||||
//mark that object already processed, so we do not serialize/deserialize duplicate objects
|
||||
isSharedProcessed = true;
|
||||
}
|
||||
};
|
||||
|
||||
struct PLCInfoSerializer: PLCInfo {
|
||||
PLCInfoSerializer(size_t id_, PointerOwnershipType ownershipType_)
|
||||
: PLCInfo(ownershipType_), id{id_} {}
|
||||
size_t id;
|
||||
};
|
||||
|
||||
struct PLCInfoDeserializer : PLCInfo {
|
||||
PLCInfoDeserializer(void *ptr, PointerOwnershipType ownershipType_)
|
||||
: PLCInfo(ownershipType_),
|
||||
ownerPtr{ptr} {};
|
||||
|
||||
//need to override these explicitly because we have pointer member
|
||||
PLCInfoDeserializer(const PLCInfoDeserializer&) = delete;
|
||||
PLCInfoDeserializer(PLCInfoDeserializer&&) = default;
|
||||
PLCInfoDeserializer& operator =(const PLCInfoDeserializer&) = delete;
|
||||
PLCInfoDeserializer& operator =(PLCInfoDeserializer&&) = default;
|
||||
|
||||
void processOwner(void *ptr) {
|
||||
ownerPtr = ptr;
|
||||
assert(ownershipType != PointerOwnershipType::Observer);
|
||||
for (auto &o:observersList)
|
||||
o.get() = ptr;
|
||||
observersList.clear();
|
||||
observersList.shrink_to_fit();
|
||||
}
|
||||
|
||||
void processObserver(void *(&ptr)) {
|
||||
if (ownerPtr) {
|
||||
ptr = ownerPtr;
|
||||
} else {
|
||||
observersList.emplace_back(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
void *ownerPtr;
|
||||
std::vector<std::reference_wrapper<void *>> observersList{};
|
||||
std::unique_ptr<PointerSharedStateBase> sharedState{};
|
||||
};
|
||||
|
||||
class PointerLinkingContextSerialization {
|
||||
public:
|
||||
explicit PointerLinkingContextSerialization()
|
||||
@@ -49,35 +146,14 @@ namespace bitsery {
|
||||
|
||||
~PointerLinkingContextSerialization() = default;
|
||||
|
||||
struct PointerInfo {
|
||||
PointerInfo(size_t id_, PointerOwnershipType ownershipType_)
|
||||
: id{id_},
|
||||
ownershipType{ownershipType_},
|
||||
sharedCount{0} {};
|
||||
size_t id;
|
||||
PointerOwnershipType ownershipType;
|
||||
size_t sharedCount;
|
||||
};
|
||||
|
||||
const PointerInfo &getInfoByPtr(const void *ptr, PointerOwnershipType ptrType) {
|
||||
auto res = _ptrMap.emplace(ptr, PointerInfo{_currId + 1u, ptrType});
|
||||
const PLCInfoSerializer &getInfoByPtr(const void *ptr, PointerOwnershipType ptrType) {
|
||||
auto res = _ptrMap.emplace(ptr, PLCInfoSerializer{_currId + 1u, ptrType});
|
||||
auto &ptrInfo = res.first->second;
|
||||
if (res.second) {
|
||||
++_currId;
|
||||
return ptrInfo;
|
||||
}
|
||||
//ptr already exists
|
||||
//for observer return success
|
||||
if (ptrType == PointerOwnershipType::Observer)
|
||||
return ptrInfo;
|
||||
//set owner and return success
|
||||
if (ptrInfo.ownershipType == PointerOwnershipType::Observer) {
|
||||
ptrInfo.ownershipType = ptrType;
|
||||
return ptrInfo;
|
||||
}
|
||||
//only shared ownership can get here multiple times
|
||||
assert(ptrType == PointerOwnershipType::Shared);
|
||||
ptrInfo.sharedCount++;
|
||||
ptrInfo.update(ptrType);
|
||||
return ptrInfo;
|
||||
}
|
||||
|
||||
@@ -85,22 +161,18 @@ namespace bitsery {
|
||||
//we cannot serialize pointers, if we haven't serialized objects themselves
|
||||
bool isPointerSerializationValid() const {
|
||||
return std::all_of(_ptrMap.begin(), _ptrMap.end(),
|
||||
[](const std::pair<const void *, PointerInfo> &p) {
|
||||
return p.second.ownershipType != PointerOwnershipType::Observer;
|
||||
[](const std::pair<const void *, PLCInfoSerializer> &p) {
|
||||
return p.second.ownershipType == PointerOwnershipType::SharedOwner ||
|
||||
p.second.ownershipType == PointerOwnershipType::Owner;
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
size_t _currId;
|
||||
std::unordered_map<const void *, PointerInfo> _ptrMap;
|
||||
std::unordered_map<const void *, PLCInfoSerializer> _ptrMap;
|
||||
|
||||
};
|
||||
|
||||
//this class is used to store context for shared ptr owners
|
||||
struct PointerSharedContextBase {
|
||||
virtual ~PointerSharedContextBase() = default;
|
||||
};
|
||||
|
||||
class PointerLinkingContextDeserialization {
|
||||
public:
|
||||
explicit PointerLinkingContextDeserialization()
|
||||
@@ -116,198 +188,194 @@ namespace bitsery {
|
||||
|
||||
~PointerLinkingContextDeserialization() = default;
|
||||
|
||||
|
||||
struct PointerInfo {
|
||||
PointerInfo(size_t id_, void *ptr, PointerOwnershipType ownershipType_)
|
||||
: id{id_},
|
||||
ownershipType{ownershipType_},
|
||||
ownerPtr{ptr},
|
||||
observersList{},
|
||||
sharedContext{} {};
|
||||
|
||||
PointerInfo(const PointerInfo &) = delete;
|
||||
|
||||
PointerInfo &operator=(const PointerInfo &) = delete;
|
||||
|
||||
PointerInfo(PointerInfo &&) = default;
|
||||
|
||||
PointerInfo &operator=(PointerInfo &&) = default;
|
||||
|
||||
~PointerInfo() = default;
|
||||
|
||||
void processOwner(void *ptr) {
|
||||
ownerPtr = ptr;
|
||||
assert(ownershipType != PointerOwnershipType::Observer);
|
||||
for (auto &o:observersList)
|
||||
o.get() = ptr;
|
||||
observersList.clear();
|
||||
observersList.shrink_to_fit();
|
||||
}
|
||||
|
||||
void processObserver(void *(&ptr)) {
|
||||
if (ownerPtr) {
|
||||
ptr = ownerPtr;
|
||||
} else {
|
||||
observersList.push_back(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
size_t id;
|
||||
PointerOwnershipType ownershipType;
|
||||
void *ownerPtr;
|
||||
std::vector<std::reference_wrapper<void *>> observersList;
|
||||
std::unique_ptr<PointerSharedContextBase> sharedContext;
|
||||
};
|
||||
|
||||
PointerInfo &getInfoById(size_t id, PointerOwnershipType ptrType) {
|
||||
auto res = _idMap.emplace(id, PointerInfo{id, nullptr, ptrType});
|
||||
PLCInfoDeserializer &getInfoById(size_t id, PointerOwnershipType ptrType) {
|
||||
auto res = _idMap.emplace(id, PLCInfoDeserializer{nullptr, ptrType});
|
||||
auto &ptrInfo = res.first->second;
|
||||
if (!res.second) {
|
||||
assert(ptrType != PointerOwnershipType::Owner ||
|
||||
ptrInfo.ownershipType == PointerOwnershipType::Observer);
|
||||
if (ptrInfo.ownershipType == PointerOwnershipType::Observer)
|
||||
ptrInfo.ownershipType = ptrType;
|
||||
}
|
||||
if (!res.second)
|
||||
ptrInfo.update(ptrType);
|
||||
return ptrInfo;
|
||||
}
|
||||
|
||||
void clearSharedState() {
|
||||
for (auto &item: _idMap)
|
||||
item.second.sharedState.reset();
|
||||
}
|
||||
|
||||
//valid, when all pointers has owners
|
||||
bool isPointerDeserializationValid() const {
|
||||
return std::all_of(_idMap.begin(), _idMap.end(), [](const std::pair<const size_t, PointerInfo> &p) {
|
||||
return p.second.ownershipType != PointerOwnershipType::Observer;
|
||||
});
|
||||
return std::all_of(_idMap.begin(), _idMap.end(),
|
||||
[](const std::pair<const size_t, PLCInfoDeserializer> &p) {
|
||||
return p.second.ownershipType == PointerOwnershipType::SharedOwner ||
|
||||
p.second.ownershipType == PointerOwnershipType::Owner;
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
std::unordered_map<size_t, PointerInfo> _idMap;
|
||||
std::unordered_map<size_t, PLCInfoDeserializer> _idMap;
|
||||
};
|
||||
|
||||
template<template<typename> class Config>
|
||||
class PointerOwnerManager {
|
||||
|
||||
template <typename TObject>
|
||||
using Handler = typename Config<TObject>::Handler;
|
||||
|
||||
template<typename TObject>
|
||||
struct HelperTypes {
|
||||
using RTTI = typename Config<TObject>::RTTI;
|
||||
using THandler = Handler<TObject>;
|
||||
using TValue = typename std::remove_pointer<typename THandler::TPointer>::type;
|
||||
};
|
||||
|
||||
template<typename Ser, typename T, typename Fnc>
|
||||
void serializeImpl(PointerLinkingContextSerialization &, Ser &ser, const T &obj, Fnc &&,
|
||||
std::true_type) const {
|
||||
InheritanceTreeSerialize<Ser, T, HelperTypes> tree{};
|
||||
auto handler = tree.getHandler(obj);
|
||||
|
||||
assert(handler.second);
|
||||
ser.object(handler.first);
|
||||
handler.second->process(ser, const_cast<T &>(obj));
|
||||
}
|
||||
|
||||
template<typename Ser, typename T, typename Fnc>
|
||||
void serializeImpl(PointerLinkingContextSerialization &, Ser &, const T &obj, Fnc &&fnc,
|
||||
std::false_type) const {
|
||||
auto handler = Handler<T>{};
|
||||
fnc(*handler.getPtr(const_cast<T &>(obj)));
|
||||
}
|
||||
|
||||
template<typename Des, typename T, typename Fnc, typename Reader>
|
||||
void deserializeImpl(PointerLinkingContextDeserialization &, Des &des, T &obj, Fnc &&,
|
||||
Reader &r, std::true_type) const {
|
||||
InheritanceTreeTypeId id{};
|
||||
des.object(id);
|
||||
|
||||
InheritanceTreeDeserialize<Des, T, HelperTypes> tree{};
|
||||
auto handler = tree.getHandler(id);
|
||||
if (handler) {
|
||||
handler->create(obj);
|
||||
handler->process(des, obj);
|
||||
} else {
|
||||
r.setError(ReaderError::InvalidPointer);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Des, typename T, typename Fnc, typename Reader>
|
||||
void deserializeImpl(PointerLinkingContextDeserialization &, Des &, T &obj, Fnc &&fnc,
|
||||
Reader &, std::false_type) const {
|
||||
using TValue = typename HelperTypes<T>::TValue;
|
||||
auto handler = Handler<T>{};
|
||||
if (auto ptr = handler.getPtr(obj)) {
|
||||
fnc(*ptr);
|
||||
} else {
|
||||
handler.template create<TValue>(obj);
|
||||
fnc(*handler.getPtr(obj));
|
||||
}
|
||||
}
|
||||
|
||||
PointerType _ptrType;
|
||||
template<template<typename> class TPtrManager,
|
||||
template<typename> class TPolymorphicContext, typename RTTI>
|
||||
class PointerObjectExtensionBase {
|
||||
public:
|
||||
|
||||
explicit PointerOwnerManager(PointerType ptrType = PointerType::Nullable) : _ptrType{ptrType} {}
|
||||
explicit PointerObjectExtensionBase(PointerType ptrType = PointerType::Nullable) :
|
||||
_ptrType{ptrType} {}
|
||||
|
||||
template<typename Ser, typename Writer, typename T, typename Fnc>
|
||||
void serialize(Ser &ser, Writer &w, const T &obj, Fnc &&fnc) const {
|
||||
auto handler = Handler<T>{};
|
||||
auto ptr = handler.getPtr(obj);
|
||||
|
||||
auto ptr = TPtrManager<T>::getPtr(const_cast<T &>(obj));
|
||||
if (ptr) {
|
||||
auto ctx = ser.template context<PointerLinkingContext>();
|
||||
assert(ctx != nullptr);
|
||||
auto &ptrInfo = ctx->getInfoByPtr(ptr, Config<T>::OwnershipType);
|
||||
auto &ptrInfo = ctx->getInfoByPtr(getBasePtr(ptr), TPtrManager<T>::getOwnership());
|
||||
details::writeSize(w, ptrInfo.id);
|
||||
if (ptrInfo.sharedCount == 0) {
|
||||
serializeImpl(*ctx, ser, obj, std::forward<Fnc>(fnc),
|
||||
std::is_polymorphic<typename HelperTypes<T>::TValue>{});
|
||||
if (TPtrManager<T>::getOwnership() != PointerOwnershipType::Observer) {
|
||||
if (!ptrInfo.isSharedProcessed)
|
||||
serializeImpl(ser, ptr, std::forward<Fnc>(fnc), w, IsPolymorphic<T>{});
|
||||
}
|
||||
} else {
|
||||
assert(_ptrType == PointerType::Nullable);
|
||||
details::writeSize(w, 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<typename Des, typename Reader, typename T, typename Fnc>
|
||||
void deserialize(Des &des, Reader &r, T &obj, Fnc &&fnc) const {
|
||||
size_t id{};
|
||||
details::readSize(r, id, std::numeric_limits<size_t>::max());
|
||||
auto handler = Handler<T>{};
|
||||
if (id) {
|
||||
auto ctx = des.template context<PointerLinkingContext>();
|
||||
assert(ctx != nullptr);
|
||||
auto &ptrInfo = ctx->getInfoById(id, Config<T>::OwnershipType);
|
||||
//todo add deserialization checking
|
||||
if (ptrInfo.ownershipType == PointerOwnershipType::Owner) {
|
||||
deserializeImpl(*ctx, des, obj, std::forward<Fnc>(fnc), r,
|
||||
std::is_polymorphic<typename HelperTypes<T>::TValue>{});
|
||||
ptrInfo.processOwner(handler.getPtr(obj));
|
||||
} else {
|
||||
if (!ptrInfo.sharedContext) {
|
||||
deserializeImpl(*ctx, des, obj, std::forward<Fnc>(fnc), r,
|
||||
std::is_polymorphic<typename HelperTypes<T>::TValue>{});
|
||||
ptrInfo.processOwner(handler.getPtr(obj));
|
||||
ptrInfo.sharedContext = Config<T>::createSharedContext(obj);
|
||||
} else {
|
||||
Config<T>::restoreFromSharedContext(obj, ptrInfo.sharedContext.get());
|
||||
}
|
||||
}
|
||||
|
||||
auto &ptrInfo = ctx->getInfoById(id, TPtrManager<T>::getOwnership());
|
||||
deserializeImpl(ptrInfo, des, obj, std::forward<Fnc>(fnc), r, IsPolymorphic<T>{},
|
||||
std::integral_constant<PointerOwnershipType, TPtrManager<T>::getOwnership()>{});
|
||||
} else {
|
||||
if (_ptrType == PointerType::Nullable && handler.getPtr(obj)) {
|
||||
handler.destroy(obj);
|
||||
if (_ptrType == PointerType::Nullable) {
|
||||
TPtrManager<T>::clear(obj);
|
||||
} else
|
||||
r.setError(ReaderError::InvalidPointer);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
template<typename T>
|
||||
struct IsPolymorphic : std::integral_constant<bool,
|
||||
RTTI::template isPolymorphic<typename TPtrManager<T>::TElement>()> {
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
const void *getBasePtr(const T *ptr) const {
|
||||
// todo implement handling of types with virtual inheritance
|
||||
// this is required to correctly track same object, when one object is derived and other is base class
|
||||
// e.g. shared_ptr<Base> and weak_ptr<Derived> or pointer observer Base*
|
||||
return ptr;
|
||||
}
|
||||
|
||||
template<typename Ser, typename TPtr, typename Fnc, typename Writer>
|
||||
void serializeImpl(Ser &ser, TPtr &ptr, Fnc &&, Writer &w, std::true_type) const {
|
||||
const auto &ctx = ser.template context<TPolymorphicContext<RTTI>>();
|
||||
ctx->serialize(ser, w, *ptr);
|
||||
}
|
||||
|
||||
template<typename Ser, typename TPtr, typename Fnc, typename Writer>
|
||||
void serializeImpl(Ser &, TPtr &ptr, Fnc &&fnc, Writer &, std::false_type) const {
|
||||
fnc(*ptr);
|
||||
}
|
||||
|
||||
template<typename Des, typename T, typename Fnc, typename Reader>
|
||||
void deserializeImpl(PLCInfoDeserializer &ptrInfo, Des &des, T &obj, Fnc &&,
|
||||
Reader &r, std::true_type ,
|
||||
std::integral_constant<PointerOwnershipType, PointerOwnershipType::Owner>) const {
|
||||
const auto &ctx = des.template context<TPolymorphicContext<RTTI>>();
|
||||
ctx->deserialize(des, r, TPtrManager<T>::getPtr(obj),
|
||||
[&obj, this](typename TPtrManager<T>::TElement *valuePtr) {
|
||||
TPtrManager<T>::assign(obj, valuePtr);
|
||||
});
|
||||
ptrInfo.processOwner(TPtrManager<T>::getPtr(obj));
|
||||
}
|
||||
|
||||
template<typename Des, typename T, typename Fnc, typename Reader>
|
||||
void deserializeImpl(PLCInfoDeserializer &ptrInfo, Des &, T &obj, Fnc &&fnc,
|
||||
Reader &, std::false_type ,
|
||||
std::integral_constant<PointerOwnershipType, PointerOwnershipType::Owner>) const {
|
||||
auto ptr = TPtrManager<T>::getPtr(obj);
|
||||
if (ptr) {
|
||||
fnc(*ptr);
|
||||
} else {
|
||||
ptr = ::bitsery::Access::createInHeap<typename TPtrManager<T>::TElement>();
|
||||
fnc(*ptr);
|
||||
TPtrManager<T>::assign(obj, ptr);
|
||||
}
|
||||
ptrInfo.processOwner(ptr);
|
||||
}
|
||||
|
||||
template<typename Des, typename T, typename Fnc, typename Reader>
|
||||
void deserializeImpl(PLCInfoDeserializer &ptrInfo, Des &des, T &obj, Fnc &&,
|
||||
Reader &r, std::true_type ,
|
||||
std::integral_constant<PointerOwnershipType, PointerOwnershipType::SharedOwner>) const {
|
||||
auto &sharedState = ptrInfo.sharedState;
|
||||
if (!sharedState) {
|
||||
const auto &ctx = des.template context<TPolymorphicContext<RTTI>>();
|
||||
ctx->deserialize(des, r, TPtrManager<T>::getPtr(obj),
|
||||
[&obj, &sharedState](typename TPtrManager<T>::TElement *valuePtr) {
|
||||
sharedState = TPtrManager<T>::createSharedState(valuePtr);
|
||||
});
|
||||
if (!sharedState)
|
||||
sharedState = TPtrManager<T>::saveToSharedState(obj);
|
||||
}
|
||||
TPtrManager<T>::loadFromSharedState(sharedState.get(), obj);
|
||||
ptrInfo.processOwner(TPtrManager<T>::getPtr(obj));
|
||||
}
|
||||
|
||||
template<typename Des, typename T, typename Fnc, typename Reader>
|
||||
void deserializeImpl(PLCInfoDeserializer &ptrInfo, Des &, T &obj, Fnc &&fnc,
|
||||
Reader &, std::false_type ,
|
||||
std::integral_constant<PointerOwnershipType, PointerOwnershipType::SharedOwner>) const {
|
||||
auto &sharedState = ptrInfo.sharedState;
|
||||
if (!sharedState) {
|
||||
if (auto ptr = TPtrManager<T>::getPtr(obj)) {
|
||||
fnc(*ptr);
|
||||
sharedState = TPtrManager<T>::saveToSharedState(obj);
|
||||
} else {
|
||||
auto res = ::bitsery::Access::createInHeap<typename TPtrManager<T>::TElement>();
|
||||
fnc(*res);
|
||||
sharedState = TPtrManager<T>::createSharedState(res);
|
||||
}
|
||||
}
|
||||
TPtrManager<T>::loadFromSharedState(sharedState.get(), obj);
|
||||
ptrInfo.processOwner(TPtrManager<T>::getPtr(obj));
|
||||
}
|
||||
|
||||
template<typename Des, typename T, typename Fnc, typename Reader, typename isPolymorph>
|
||||
void deserializeImpl(PLCInfoDeserializer &ptrInfo, Des &des, T &obj, Fnc &&fnc,
|
||||
Reader &r, isPolymorph polymorph,
|
||||
std::integral_constant<PointerOwnershipType, PointerOwnershipType::SharedObserver>) const {
|
||||
deserializeImpl(ptrInfo, des, obj, fnc, r, polymorph,
|
||||
std::integral_constant<PointerOwnershipType, PointerOwnershipType::SharedOwner>{});
|
||||
}
|
||||
|
||||
template<typename Des, typename T, typename Fnc, typename Reader, typename isPolymorphic>
|
||||
void deserializeImpl(PLCInfoDeserializer &ptrInfo, Des &, T &obj, Fnc &&,
|
||||
Reader &, isPolymorphic,
|
||||
std::integral_constant<PointerOwnershipType, PointerOwnershipType::Observer>) const {
|
||||
ptrInfo.processObserver(reinterpret_cast<void *&>(TPtrManager<T>::getPtrRef(obj)));
|
||||
}
|
||||
|
||||
PointerType _ptrType;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
//this class is for convenience
|
||||
class PointerLinkingContext :
|
||||
public utils::PointerLinkingContextSerialization,
|
||||
public utils::PointerLinkingContextDeserialization {
|
||||
public pointer_utils::PointerLinkingContextSerialization,
|
||||
public pointer_utils::PointerLinkingContextDeserialization {
|
||||
public:
|
||||
explicit PointerLinkingContext() = default;
|
||||
bool isValid() {
|
||||
return isPointerSerializationValid() && isPointerDeserializationValid();
|
||||
}
|
||||
|
||||
@@ -1,225 +1,238 @@
|
||||
//MIT License
|
||||
//
|
||||
// Created by fraillt on 17.11.3.
|
||||
//Copyright (c) 2018 Mindaugas Vinkelis
|
||||
//
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
//of this software and associated documentation files (the "Software"), to deal
|
||||
//in the Software without restriction, including without limitation the rights
|
||||
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
//copies of the Software, and to permit persons to whom the Software is
|
||||
//furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//The above copyright notice and this permission notice shall be included in all
|
||||
//copies or substantial portions of the Software.
|
||||
//
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_EXT_POLYMORPHISM_H
|
||||
#define BITSERY_EXT_POLYMORPHISM_H
|
||||
|
||||
#include "../inheritance.h"
|
||||
#ifndef BITSERY_EXT_POLYMORPHISM_UTILS_H
|
||||
#define BITSERY_EXT_POLYMORPHISM_UTILS_H
|
||||
|
||||
#include <unordered_map>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include "../../details/adapter_common.h"
|
||||
#include "../../details/serialization_common.h"
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
namespace ext {
|
||||
namespace details_polymorphism {
|
||||
//stores template types
|
||||
template<typename ...>
|
||||
struct List {
|
||||
};
|
||||
}
|
||||
|
||||
//specialize for your base class by deriving from DerivedClasses with list of derivatives that DIRECTLY inherits from your base class.
|
||||
//helper type, that contains list of types
|
||||
template<typename ...>
|
||||
struct PolymorphicClassesList {
|
||||
};
|
||||
|
||||
//specialize for your base class by deriving from PolymorphicDerivedClasses with list of derivatives that DIRECTLY inherits from your base class.
|
||||
//e.g.
|
||||
// template <> PolymorphicBase<Animal>: DerivedClasses<Dog, Cat>{};
|
||||
// template <> PolymorphicBase<Dog>: DerivedClasses<Bulldog, GoldenRetriever> {};
|
||||
// template <> PolymorphicBaseClass<Animal>: PolymorphicDerivedClasses<Dog, Cat>{};
|
||||
// template <> PolymorphicBaseClass<Dog>: PolymorphicDerivedClasses<Bulldog, GoldenRetriever> {};
|
||||
// IMPORTANT !!!
|
||||
// although you can add all derivates to same base like this:
|
||||
// SuperClass<Animal>:DerivedClasses<Dog, Cat, Bulldog, GoldenRetriever>{};
|
||||
// template <> PolymorphicBaseClass<Animal>:PolymorphicDerivedClasses<Dog, Cat, Bulldog, GoldenRetriever>{};
|
||||
// it will not work when you try to serialize Dog*, because it will not find Bulldog and GoldenRetriever
|
||||
template<typename TBase>
|
||||
struct PolymorphicBaseClass {
|
||||
using childs = details_polymorphism::List<>;
|
||||
using Childs = PolymorphicClassesList<>;
|
||||
};
|
||||
|
||||
//derive from this class when specifying childs for your base class, atleast one child must exists, hence T1
|
||||
//e.g.
|
||||
// template <> SuperClass<Animal>: DerivedClasses<Dog, Cat>{};
|
||||
// template <> PolymorphicBaseClass<Animal>: PolymorphicDerivedClasses<Dog, Cat>{};
|
||||
template<typename T1, typename ... Tn>
|
||||
struct DerivedClasses {
|
||||
using childs = details_polymorphism::List<T1, Tn...>;
|
||||
struct PolymorphicDerivedClasses {
|
||||
using Childs = PolymorphicClassesList<T1, Tn...>;
|
||||
};
|
||||
|
||||
namespace utils {
|
||||
//this object will be used to serialize/deserialize polymorphic type id within inheritance tree from base class
|
||||
struct InheritanceTreeTypeId {
|
||||
uint16_t depth{};
|
||||
uint16_t index{};
|
||||
template <typename S>
|
||||
void serialize(S& s) {
|
||||
s.value2b(depth);
|
||||
s.value2b(index);
|
||||
class PolymorphicHandlerBase {
|
||||
public:
|
||||
virtual void *create() const = 0;
|
||||
virtual void process(void *ser, void *obj) const = 0;
|
||||
virtual ~PolymorphicHandlerBase() = default;
|
||||
};
|
||||
|
||||
template<typename RTTI, typename TSerializer, typename TBase, typename TDerived>
|
||||
class PolymorphicHandler : public PolymorphicHandlerBase {
|
||||
public:
|
||||
|
||||
void *create() const final {
|
||||
return toBase(::bitsery::Access::createInHeap<TDerived>());
|
||||
}
|
||||
|
||||
void process(void *ser, void *obj) const final {
|
||||
static_cast<TSerializer *>(ser)->object(*static_cast<TDerived *>(fromBase(obj)));
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void *fromBase(void *obj) const {
|
||||
return RTTI::template cast<TBase, TDerived>(static_cast<TBase *>(obj));
|
||||
}
|
||||
|
||||
void *toBase(void *obj) const {
|
||||
return RTTI::template cast<TDerived, TBase>(static_cast<TDerived *>(obj));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template<typename RTTI>
|
||||
class PolymorphicContext {
|
||||
private:
|
||||
|
||||
struct BaseToDerivedKey {
|
||||
|
||||
std::size_t baseHash;
|
||||
std::size_t derivedHash;
|
||||
|
||||
bool operator==(const BaseToDerivedKey &other) const {
|
||||
return baseHash == other.baseHash && derivedHash == other.derivedHash;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename S, typename TObject>
|
||||
struct PolymorphicObjectHandlerInterface {
|
||||
virtual void process(S& s, TObject& obj) const = 0;
|
||||
virtual void create(TObject& obj) const = 0;
|
||||
virtual ~PolymorphicObjectHandlerInterface() = default;
|
||||
struct BaseToDerivedKeyHashier {
|
||||
size_t operator()(const BaseToDerivedKey &key) const {
|
||||
return (key.baseHash + (key.baseHash << 6) + (key.derivedHash >> 2)) ^ key.derivedHash;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
namespace details_polymorphism {
|
||||
template<typename TSerializer, template<typename> class THierarchy, typename TBase, typename TDerived>
|
||||
void add() {
|
||||
addToMap<TSerializer, TBase, TDerived>(std::is_abstract<TDerived>{});
|
||||
addChilds<TSerializer, THierarchy, TBase, TDerived>(typename THierarchy<TDerived>::Childs{});
|
||||
}
|
||||
|
||||
template<typename TSerializer, template<typename> class THierarchy, typename TBase, typename TDerived, typename T1, typename ... Tn>
|
||||
void addChilds(PolymorphicClassesList<T1, Tn...>) {
|
||||
static_assert(std::is_base_of<TDerived, T1>::value,
|
||||
"PolymorphicBaseClass<TBase> must derive a list of derived classes from TBase.");
|
||||
add<TSerializer, THierarchy, TBase, T1>();
|
||||
addChilds<TSerializer, THierarchy, TBase, TDerived>(PolymorphicClassesList<Tn...>{});
|
||||
//iterate through derived class hierarchy as well
|
||||
add<TSerializer, THierarchy, T1, T1>();
|
||||
}
|
||||
|
||||
template<typename TSerializer, template<typename> class THierarchy, typename TBase, typename TDerived>
|
||||
void addChilds(PolymorphicClassesList<>) {
|
||||
}
|
||||
|
||||
template<typename TSerializer, typename TBase, typename TDerived>
|
||||
void addToMap(std::false_type) {
|
||||
BaseToDerivedKey key{RTTI::template get<TBase>(), RTTI::template get<TDerived>()};
|
||||
if (_baseToDerivedMap.emplace(key, std::unique_ptr<PolymorphicHandlerBase>(
|
||||
new PolymorphicHandler<RTTI, TSerializer, TBase, TDerived>{})).second)
|
||||
_baseToDerivedArray[key.baseHash].push_back(key.derivedHash);
|
||||
}
|
||||
|
||||
template<typename TSerializer, typename TBase, typename TDerived>
|
||||
void addToMap(std::true_type) {
|
||||
//cannot add abstract class
|
||||
}
|
||||
|
||||
std::unordered_map<BaseToDerivedKey, std::unique_ptr<PolymorphicHandlerBase>, BaseToDerivedKeyHashier> _baseToDerivedMap{};
|
||||
// this will allow convert from platform specific type information, to platform independent base->derived index
|
||||
// this only works if all polymorphic relationships (PolymorphicBaseClass<TBase> -> PolymorphicDerivedClasses<TDerived...>)
|
||||
// is equal between platforms.
|
||||
std::unordered_map<size_t, std::vector<size_t>> _baseToDerivedArray{};
|
||||
|
||||
public:
|
||||
|
||||
void clear() {
|
||||
_baseToDerivedMap.clear();
|
||||
_baseToDerivedArray.clear();
|
||||
}
|
||||
|
||||
template<typename TSerializer, template<typename> class THierarchy = PolymorphicBaseClass, typename T1, typename ...Tn>
|
||||
[[deprecated("de/serializer instance is not required")]] void registerBasesList(const TSerializer &s, PolymorphicClassesList<T1, Tn...>) {
|
||||
add<TSerializer, THierarchy, T1, T1>();
|
||||
registerBasesList<TSerializer, THierarchy>(s, PolymorphicClassesList<Tn...>{});
|
||||
}
|
||||
|
||||
template<typename TSerializer, template<typename> class THierarchy>
|
||||
[[deprecated]] void registerBasesList(const TSerializer &, PolymorphicClassesList<>) {
|
||||
}
|
||||
|
||||
// THierarchy is the name of class, that defines hierarchy
|
||||
// PolymorphicBaseClass is defined as default parameter, so that at instantiation time
|
||||
// it will get unique symbol in translation unit for PolymorphicBaseClass (which is defined in anonymous namespace)
|
||||
// https://github.com/fraillt/bitsery/issues/9
|
||||
template<typename TSerializer, template<typename> class THierarchy = PolymorphicBaseClass, typename T1, typename ...Tn>
|
||||
void registerBasesList(PolymorphicClassesList<T1, Tn...>) {
|
||||
add<TSerializer, THierarchy, T1, T1>();
|
||||
registerBasesList<TSerializer, THierarchy>(PolymorphicClassesList<Tn...>{});
|
||||
}
|
||||
|
||||
template<typename TSerializer, template<typename> class THierarchy>
|
||||
void registerBasesList(PolymorphicClassesList<>) {
|
||||
}
|
||||
|
||||
// optional method, in case you want to construct base class hierarchy your self
|
||||
template <typename TSerializer, typename TBase, typename TDerived>
|
||||
void registerSingleBaseBranch() {
|
||||
static_assert(std::is_base_of<TBase, TDerived>::value, "TDerived must be derived from TBase");
|
||||
static_assert(!std::is_abstract<TDerived>::value, "TDerived cannot be abstract");
|
||||
addToMap<TSerializer, TBase, TDerived>(std::false_type{});
|
||||
}
|
||||
|
||||
|
||||
template<typename S, typename TObject, typename TDerived, typename RTTI, typename ObjectHandler>
|
||||
struct PolymorphicObjectHandlerBase : public utils::PolymorphicObjectHandlerInterface<S, TObject> {
|
||||
void process(S &s, TObject &obj) const final {
|
||||
s.object(dynamic_cast<TDerived &>(*_handler.getPtr(obj)));
|
||||
};
|
||||
template<typename Serializer, typename Writer, typename TBase>
|
||||
void serialize(Serializer &ser, Writer &writer, TBase &obj) {
|
||||
//get derived key
|
||||
BaseToDerivedKey key{RTTI::template get<TBase>(), RTTI::template get<TBase>(obj)};
|
||||
auto it = _baseToDerivedMap.find(key);
|
||||
assert(it != _baseToDerivedMap.end());
|
||||
|
||||
void create(TObject &obj) const final {
|
||||
auto ptr = _handler.getPtr(obj);
|
||||
if (ptr && RTTI::get(*ptr) != RTTI::template get<TDerived>()) {
|
||||
_handler.destroy(obj);
|
||||
_handler.template create<TDerived>(obj);
|
||||
} else {
|
||||
if (ptr == nullptr)
|
||||
_handler.template create<TDerived>(obj);
|
||||
//convert derived hash to derived index, to make it work in cross-platform environment
|
||||
auto &vec = _baseToDerivedArray.find(key.baseHash)->second;
|
||||
auto derivedIndex = static_cast<size_t>(std::distance(vec.begin(), std::find(vec.begin(), vec.end(),
|
||||
key.derivedHash)));
|
||||
details::writeSize(writer, derivedIndex);
|
||||
|
||||
//serialize
|
||||
it->second->process(&ser, &obj);
|
||||
}
|
||||
|
||||
template<typename Deserializer, typename Reader, typename TBase, typename TAssignFnc>
|
||||
void deserialize(Deserializer &des, Reader &reader, TBase *obj, TAssignFnc assignFnc) {
|
||||
size_t derivedIndex{};
|
||||
details::readSize(reader, derivedIndex, std::numeric_limits<size_t>::max());
|
||||
|
||||
auto baseToDerivedVecIt = _baseToDerivedArray.find(RTTI::template get<TBase>());
|
||||
//base class is known at compile time, so we can assert on this one
|
||||
assert(baseToDerivedVecIt != _baseToDerivedArray.end());
|
||||
|
||||
if (baseToDerivedVecIt->second.size() > derivedIndex) {
|
||||
//convert derived index to derived hash, to make it work in cross-platform environment
|
||||
auto derivedHash = baseToDerivedVecIt->second[derivedIndex];
|
||||
auto &handler = _baseToDerivedMap.find(
|
||||
BaseToDerivedKey{RTTI::template get<TBase>(), derivedHash})->second;
|
||||
//if object is null or different type, create new and assign it
|
||||
if (obj == nullptr || RTTI::template get<TBase>(*obj) != derivedHash) {
|
||||
obj = static_cast<TBase *>(handler->create());
|
||||
assignFnc(obj);
|
||||
}
|
||||
}
|
||||
ObjectHandler _handler{};
|
||||
};
|
||||
|
||||
template<typename S, typename TObject, template<typename> class TObjectManager>
|
||||
class PolymorphicHandlersGenerator {
|
||||
public:
|
||||
template<typename Fnc>
|
||||
static void generate(Fnc &&addHandlerFnc) {
|
||||
PolymorphicHandlersGenerator tmp{std::forward<Fnc>(addHandlerFnc)};
|
||||
}
|
||||
|
||||
private:
|
||||
using RTTI = typename TObjectManager<TObject>::RTTI;
|
||||
|
||||
template <typename TDerived>
|
||||
using TPolymorphicObjectHandler = PolymorphicObjectHandlerBase<S, TObject, TDerived, RTTI, typename TObjectManager<TObject>::THandler>;
|
||||
|
||||
template<typename Fnc>
|
||||
explicit PolymorphicHandlersGenerator(Fnc &&addHandlerFnc):_addHandler(std::forward<Fnc>(addHandlerFnc)) {
|
||||
//fill inheritance tree
|
||||
using TBase = typename TObjectManager<TObject>::TValue;
|
||||
add<TBase>();
|
||||
}
|
||||
|
||||
template<typename TClass>
|
||||
void add() {
|
||||
addClass<TClass>();
|
||||
//save current index and increase depth
|
||||
auto saveIndex = index;
|
||||
index = 0;
|
||||
depth++;
|
||||
addChilds<TClass>(typename PolymorphicBaseClass<TClass>::childs{});
|
||||
//restore index and depth
|
||||
depth--;
|
||||
index = saveIndex;
|
||||
}
|
||||
|
||||
template<typename TClassBase, typename T1, typename ... Tn>
|
||||
void addChilds(details_polymorphism::List<T1, Tn...>) {
|
||||
static_assert(std::is_base_of<TClassBase, T1>::value,
|
||||
"PolymorphicBaseClass<TBase> must derive a list of derived classes from TBase.");
|
||||
add<T1>();
|
||||
addChilds<TClassBase>(details_polymorphism::List<Tn...>{});
|
||||
}
|
||||
|
||||
template<typename>
|
||||
void addChilds(details_polymorphism::List<>) {
|
||||
}
|
||||
|
||||
|
||||
template<typename TClass>
|
||||
void addClass() {
|
||||
if (!std::is_abstract<TClass>::value) {
|
||||
utils::InheritanceTreeTypeId tid{};
|
||||
tid.index = index;
|
||||
tid.depth = depth;
|
||||
#if __cplusplus > 201103L
|
||||
auto handler = std::make_unique<TPolymorphicObjectHandler<TClass>>();
|
||||
#else
|
||||
auto handler = std::unique_ptr<TPolymorphicObjectHandler<TClass>>(
|
||||
new TPolymorphicObjectHandler<TClass>{});
|
||||
#endif
|
||||
if (_addHandler(RTTI::template get<TClass>(), tid, std::move(handler)))
|
||||
++index;
|
||||
}
|
||||
}
|
||||
|
||||
std::function<bool(size_t, utils::InheritanceTreeTypeId,
|
||||
std::unique_ptr<utils::PolymorphicObjectHandlerInterface<S, TObject>> &&)> _addHandler;
|
||||
uint16_t depth{};
|
||||
uint16_t index{};
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace utils {
|
||||
|
||||
template<typename Ser, typename TObject, template<typename> class TObjectManager>
|
||||
class InheritanceTreeSerialize {
|
||||
public:
|
||||
|
||||
InheritanceTreeSerialize() {
|
||||
details_polymorphism::PolymorphicHandlersGenerator<Ser, TObject, TObjectManager>::generate(
|
||||
[this](size_t typeHash, InheritanceTreeTypeId id,
|
||||
std::unique_ptr<PolymorphicObjectHandlerInterface<Ser, TObject>> &&handler) {
|
||||
return _map.emplace(std::make_pair(typeHash, std::make_pair(id, std::move(handler)))).second;
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
const std::pair<InheritanceTreeTypeId, PolymorphicObjectHandlerInterface<Ser, TObject>*> getHandler(const TObject &obj) {
|
||||
using RTTI = typename TObjectManager<TObject>::RTTI;
|
||||
auto handler = typename TObjectManager<TObject>::THandler{};
|
||||
auto it = _map.find(RTTI::get(*handler.getPtr(obj)));
|
||||
if (it != _map.end()) {
|
||||
return std::make_pair(it->second.first, it->second.second.get());
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
private:
|
||||
std::unordered_map<size_t, std::pair<InheritanceTreeTypeId, std::unique_ptr<PolymorphicObjectHandlerInterface<Ser, TObject>>>> _map{};
|
||||
};
|
||||
|
||||
|
||||
template<typename Des, typename TObject, template<typename> class TObjectManager>
|
||||
class InheritanceTreeDeserialize {
|
||||
public:
|
||||
InheritanceTreeDeserialize() {
|
||||
details_polymorphism::PolymorphicHandlersGenerator<Des, TObject, TObjectManager>::generate(
|
||||
[this](size_t, InheritanceTreeTypeId id, std::unique_ptr<PolymorphicObjectHandlerInterface<Des, TObject>> &&handler) {
|
||||
return _map.emplace(std::make_pair(getHashFromId(id), std::move(handler))).second;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const PolymorphicObjectHandlerInterface<Des, TObject> *getHandler(const InheritanceTreeTypeId &id) {
|
||||
auto it = _map.find(getHashFromId(id));
|
||||
if (it != _map.end())
|
||||
return it->second.get();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t getHashFromId(const InheritanceTreeTypeId &id) const {
|
||||
size_t res = id.depth << 16;
|
||||
return res + id.index;
|
||||
}
|
||||
|
||||
std::unordered_map<size_t, std::unique_ptr<PolymorphicObjectHandlerInterface<Des, TObject>>> _map{};
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
handler->process(&des, obj);
|
||||
} else
|
||||
reader.setError(ReaderError::InvalidPointer);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif //BITSERY_EXT_POLYMORPHISM_H
|
||||
#endif //BITSERY_EXT_POLYMORPHISM_UTILS_H
|
||||
|
||||
@@ -1,27 +1,64 @@
|
||||
//MIT License
|
||||
//
|
||||
// Created by fraillt on 17.11.30.
|
||||
//Copyright (c) 2018 Mindaugas Vinkelis
|
||||
//
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
//of this software and associated documentation files (the "Software"), to deal
|
||||
//in the Software without restriction, including without limitation the rights
|
||||
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
//copies of the Software, and to permit persons to whom the Software is
|
||||
//furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//The above copyright notice and this permission notice shall be included in all
|
||||
//copies or substantial portions of the Software.
|
||||
//
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_RTTI_UTILS_H
|
||||
#define BITSERY_RTTI_UTILS_H
|
||||
|
||||
#include <typeinfo>
|
||||
#include <type_traits>
|
||||
#include <cstddef>
|
||||
|
||||
namespace bitsery {
|
||||
namespace ext {
|
||||
namespace utils {
|
||||
struct StandardRTTI {
|
||||
template<typename T>
|
||||
static size_t get() {
|
||||
return typeid(T).hash_code();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static size_t get(T &&obj) {
|
||||
return typeid(obj).hash_code();
|
||||
}
|
||||
};
|
||||
}
|
||||
struct StandardRTTI {
|
||||
|
||||
// static_assert(!std::is_pointer<TBase>::value &&
|
||||
// !std::is_const<TBase>::value &&
|
||||
// !std::is_volatile<TBase>::value, "");
|
||||
|
||||
template<typename TBase>
|
||||
static size_t get(TBase &obj) {
|
||||
return typeid(obj).hash_code();
|
||||
}
|
||||
|
||||
template<typename TBase>
|
||||
static constexpr size_t get() {
|
||||
return typeid(TBase).hash_code();
|
||||
}
|
||||
|
||||
template<typename TBase, typename TDerived>
|
||||
static constexpr TDerived *cast(TBase *obj) {
|
||||
static_assert(!std::is_pointer<TDerived>::value, "");
|
||||
return dynamic_cast<TDerived *>(obj);
|
||||
}
|
||||
|
||||
template<typename TBase>
|
||||
static constexpr bool isPolymorphic() {
|
||||
return std::is_polymorphic<TBase>::value;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -96,7 +96,8 @@ namespace bitsery {
|
||||
constexpr RangeSpec(T minValue, T maxValue, T precision) :
|
||||
min{minValue},
|
||||
max{maxValue},
|
||||
bitsRequired{calcRequiredBits<details::SameSizeUnsigned<T>>({}, ((max - min) / precision))} {
|
||||
bitsRequired{calcRequiredBits<details::SameSizeUnsigned<T>>(
|
||||
{}, static_cast<details::SameSizeUnsigned<T>>((max - min) / precision))} {
|
||||
|
||||
}
|
||||
|
||||
@@ -163,7 +164,8 @@ namespace bitsery {
|
||||
public:
|
||||
|
||||
template<typename ... Args>
|
||||
explicit constexpr ValueRange(Args &&... args):_range{std::forward<Args>(args)...} {}
|
||||
constexpr ValueRange(const TValue& min, const TValue& max, Args &&... args)
|
||||
:_range{min, max, std::forward<Args>(args)...} {}
|
||||
|
||||
template<typename Ser, typename Writer, typename T, typename Fnc>
|
||||
void serialize(Ser &, Writer &writer, const T &v, Fnc &&) const {
|
||||
|
||||
@@ -28,8 +28,8 @@
|
||||
#include "../details/flexible_common.h"
|
||||
|
||||
namespace bitsery {
|
||||
template<typename S, typename ... TArgs>
|
||||
void serialize(S &s, std::deque<TArgs... > &obj) {
|
||||
template<typename S, typename T, typename Allocator>
|
||||
void serialize(S &s, std::deque<T, Allocator> &obj) {
|
||||
flexible::processContainer(s, obj);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,8 +28,8 @@
|
||||
#include "../details/flexible_common.h"
|
||||
|
||||
namespace bitsery {
|
||||
template<typename S, typename ... TArgs>
|
||||
void serialize(S &s, std::forward_list<TArgs... > &obj) {
|
||||
template<typename S, typename T, typename Allocator>
|
||||
void serialize(S &s, std::forward_list<T, Allocator> &obj) {
|
||||
flexible::processContainer(s, obj);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,8 +28,8 @@
|
||||
#include "../details/flexible_common.h"
|
||||
|
||||
namespace bitsery {
|
||||
template<typename S, typename ... TArgs>
|
||||
void serialize(S &s, std::list<TArgs... > &obj) {
|
||||
template<typename S, typename T, typename Allocator>
|
||||
void serialize(S &s, std::list<T, Allocator> &obj) {
|
||||
flexible::processContainer(s, obj);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,23 +28,19 @@
|
||||
#include "../ext/std_map.h"
|
||||
|
||||
namespace bitsery {
|
||||
template<typename S, typename ... TArgs>
|
||||
void serialize(S &s, std::map<TArgs ... > &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
|
||||
using TKey = typename std::map<TArgs...>::key_type;
|
||||
using TValue = typename std::map<TArgs...>::mapped_type;
|
||||
template<typename S, typename Key, typename T, typename Compare, typename Allocator>
|
||||
void serialize(S &s, std::map<Key, T, Compare, Allocator> &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
|
||||
s.ext(obj, ext::StdMap{maxSize},
|
||||
[&s](TKey& key, TValue& value) {
|
||||
[&s](Key& key, T& value) {
|
||||
s.object(key);
|
||||
s.object(value);
|
||||
});
|
||||
}
|
||||
|
||||
template<typename S, typename ... TArgs>
|
||||
void serialize(S &s, std::multimap<TArgs ... > &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
|
||||
using TKey = typename std::multimap<TArgs...>::key_type;
|
||||
using TValue = typename std::multimap<TArgs...>::mapped_type;
|
||||
template<typename S, typename Key, typename T, typename Compare, typename Allocator>
|
||||
void serialize(S &s, std::multimap<Key, T, Compare, Allocator> &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
|
||||
s.ext(obj, ext::StdMap{maxSize},
|
||||
[&s](TKey& key, TValue& value) {
|
||||
[&s](Key& key, T& value) {
|
||||
s.object(key);
|
||||
s.object(value);
|
||||
});
|
||||
|
||||
45
include/bitsery/flexible/memory.h
Normal file
45
include/bitsery/flexible/memory.h
Normal file
@@ -0,0 +1,45 @@
|
||||
//MIT License
|
||||
//
|
||||
//Copyright (c) 2018 Mindaugas Vinkelis
|
||||
//
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
//of this software and associated documentation files (the "Software"), to deal
|
||||
//in the Software without restriction, including without limitation the rights
|
||||
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
//copies of the Software, and to permit persons to whom the Software is
|
||||
//furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//The above copyright notice and this permission notice shall be included in all
|
||||
//copies or substantial portions of the Software.
|
||||
//
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_FLEXIBLE_TYPE_STD_MEMORY_H
|
||||
#define BITSERY_FLEXIBLE_TYPE_STD_MEMORY_H
|
||||
|
||||
#include "../ext/std_smart_ptr.h"
|
||||
|
||||
namespace bitsery {
|
||||
template<typename S, typename T, typename D>
|
||||
void serialize(S &s, std::unique_ptr<T, D> &obj) {
|
||||
s.ext(obj, ext::StdSmartPtr{});
|
||||
}
|
||||
|
||||
template<typename S, typename T>
|
||||
void serialize(S &s, std::shared_ptr<T> &obj) {
|
||||
s.ext(obj, ext::StdSmartPtr{});
|
||||
}
|
||||
|
||||
template<typename S, typename T>
|
||||
void serialize(S &s, std::weak_ptr<T> &obj) {
|
||||
s.ext(obj, ext::StdSmartPtr{});
|
||||
}
|
||||
}
|
||||
|
||||
#endif //BITSERY_FLEXIBLE_TYPE_STD_MEMORY_H
|
||||
@@ -28,13 +28,13 @@
|
||||
#include "../ext/std_set.h"
|
||||
|
||||
namespace bitsery {
|
||||
template<typename S, typename ... TArgs>
|
||||
void serialize(S &s, std::set<TArgs...> &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
|
||||
template<typename S, typename Key, typename Compare, typename Allocator>
|
||||
void serialize(S &s, std::set<Key, Compare, Allocator> &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
|
||||
s.ext(obj, ext::StdSet{maxSize});
|
||||
}
|
||||
|
||||
template<typename S, typename ... TArgs>
|
||||
void serialize(S &s, std::multiset<TArgs...> &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
|
||||
template<typename S, typename Key, typename Compare, typename Allocator>
|
||||
void serialize(S &s, std::multiset<Key, Compare, Allocator> &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
|
||||
s.ext(obj, ext::StdSet{maxSize});
|
||||
}
|
||||
|
||||
|
||||
@@ -28,8 +28,8 @@
|
||||
#include "../details/flexible_common.h"
|
||||
|
||||
namespace bitsery {
|
||||
template<typename S, typename T, typename ... TArgs>
|
||||
void serialize(S &s, std::basic_string<T, TArgs...> &str) {
|
||||
template<typename S, typename CharT, typename Traits, typename Allocator>
|
||||
void serialize(S &s, std::basic_string<CharT, Traits, Allocator> &str) {
|
||||
flexible::processContainer(s, str);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,23 +28,19 @@
|
||||
#include "../ext/std_map.h"
|
||||
|
||||
namespace bitsery {
|
||||
template<typename S, typename ... TArgs>
|
||||
void serialize(S &s, std::unordered_map<TArgs ... > &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
|
||||
using TKey = typename std::unordered_map<TArgs...>::key_type;
|
||||
using TValue = typename std::unordered_map<TArgs...>::mapped_type;
|
||||
template<typename S, typename Key, typename T, typename Hash, typename KeyEqual, typename Allocator>
|
||||
void serialize(S &s, std::unordered_map<Key, T, Hash, KeyEqual, Allocator> &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
|
||||
s.ext(obj, ext::StdMap{maxSize},
|
||||
[&s](TKey& key, TValue& value) {
|
||||
[&s](Key& key, T& value) {
|
||||
s.object(key);
|
||||
s.object(value);
|
||||
});
|
||||
}
|
||||
|
||||
template<typename S, typename ... TArgs>
|
||||
void serialize(S &s, std::unordered_multimap<TArgs ... > &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
|
||||
using TKey = typename std::unordered_multimap<TArgs...>::key_type;
|
||||
using TValue = typename std::unordered_multimap<TArgs...>::mapped_type;
|
||||
template<typename S, typename Key, typename T, typename Hash, typename KeyEqual, typename Allocator>
|
||||
void serialize(S &s, std::unordered_multimap<Key, T, Hash, KeyEqual, Allocator> &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
|
||||
s.ext(obj, ext::StdMap{maxSize},
|
||||
[&s](TKey& key, TValue& value) {
|
||||
[&s](Key& key, T& value) {
|
||||
s.object(key);
|
||||
s.object(value);
|
||||
});
|
||||
|
||||
@@ -28,13 +28,13 @@
|
||||
#include "../ext/std_set.h"
|
||||
|
||||
namespace bitsery {
|
||||
template<typename S, typename ... TArgs>
|
||||
void serialize(S &s, std::unordered_set<TArgs...> &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
|
||||
template<typename S, typename Key, typename Hash, typename KeyEqual, typename Allocator>
|
||||
void serialize(S &s, std::unordered_set<Key, Hash, KeyEqual, Allocator> &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
|
||||
s.ext(obj, ext::StdSet{maxSize});
|
||||
}
|
||||
|
||||
template<typename S, typename ... TArgs>
|
||||
void serialize(S &s, std::unordered_multiset<TArgs...> &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
|
||||
template<typename S, typename Key, typename Hash, typename KeyEqual, typename Allocator>
|
||||
void serialize(S &s, std::unordered_multiset<Key, Hash, KeyEqual, Allocator> &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
|
||||
s.ext(obj, ext::StdSet{maxSize});
|
||||
}
|
||||
|
||||
|
||||
@@ -28,8 +28,8 @@
|
||||
#include "../details/flexible_common.h"
|
||||
|
||||
namespace bitsery {
|
||||
template<typename S, typename ... TArgs>
|
||||
void serialize(S &s, std::vector<TArgs... > &obj) {
|
||||
template<typename S, typename T, typename Allocator>
|
||||
void serialize(S &s, std::vector<T, Allocator> &obj) {
|
||||
flexible::processContainer(s, obj);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
#define BITSERY_TRAITS_CORE_STD_DEFAULTS_H
|
||||
|
||||
#include "traits.h"
|
||||
#include <iostream>
|
||||
#include "../../details/serialization_common.h"
|
||||
|
||||
namespace bitsery {
|
||||
namespace traits {
|
||||
@@ -53,13 +53,28 @@ namespace bitsery {
|
||||
return container.size();
|
||||
}
|
||||
static void resize(T& container, size_t size) {
|
||||
resizeImpl(container, size, std::is_default_constructible<TValue>{});
|
||||
}
|
||||
private:
|
||||
static void resizeImpl(T& container, size_t size, std::true_type) {
|
||||
container.resize(size);
|
||||
}
|
||||
static void resizeImpl(T& container, size_t newSize, std::false_type) {
|
||||
const auto oldSize = size(container);
|
||||
for (auto it = oldSize; it < newSize; ++it) {
|
||||
container.push_back(::bitsery::Access::create<TValue>());
|
||||
}
|
||||
if (oldSize > newSize) {
|
||||
container.erase(std::next(std::begin(container), newSize));
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template <typename T, bool Resizable = ContainerTraits<T>::isResizable>
|
||||
struct StdContainerForBufferAdapter {
|
||||
using TIterator = typename T::iterator;
|
||||
using TConstIterator = typename T::const_iterator;
|
||||
using TValue = typename ContainerTraits<T>::TValue;
|
||||
};
|
||||
|
||||
@@ -73,9 +88,10 @@ namespace bitsery {
|
||||
auto newSize = static_cast<size_t>(container.size() * 1.5 + 128);
|
||||
//make data cache friendly
|
||||
newSize -= newSize % 64;//64 is cache line size
|
||||
container.resize(std::max(newSize, container.capacity()));
|
||||
container.resize((std::max)(newSize, container.capacity()));
|
||||
}
|
||||
using TIterator = typename T::iterator;
|
||||
using TConstIterator = typename T::const_iterator;
|
||||
using TValue = typename ContainerTraits<T>::TValue;
|
||||
};
|
||||
|
||||
|
||||
@@ -155,6 +155,7 @@ namespace bitsery {
|
||||
}
|
||||
|
||||
using TIterator = details::NotDefinedType;
|
||||
using TConstIterator = details::NotDefinedType;
|
||||
using TValue = typename ContainerTraits<T>::TValue;
|
||||
};
|
||||
|
||||
@@ -162,6 +163,7 @@ namespace bitsery {
|
||||
template <typename T, size_t N>
|
||||
struct BufferAdapterTraits<T[N]> {
|
||||
using TIterator = T*;
|
||||
using TConstIterator = const T*;
|
||||
using TValue = T;
|
||||
};
|
||||
|
||||
@@ -169,12 +171,14 @@ namespace bitsery {
|
||||
template <typename T>
|
||||
struct BufferAdapterTraits<const T*> {
|
||||
using TIterator = const T*;
|
||||
using TConstIterator = const T*;
|
||||
using TValue = T;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct BufferAdapterTraits<T*> {
|
||||
using TIterator = T*;
|
||||
using TConstIterator = const T*;
|
||||
using TValue = T;
|
||||
};
|
||||
|
||||
|
||||
@@ -31,9 +31,9 @@ namespace bitsery {
|
||||
|
||||
namespace traits {
|
||||
|
||||
template<typename ... TArgs>
|
||||
struct ContainerTraits<std::deque<TArgs...>>
|
||||
: public StdContainer<std::deque<TArgs...>, true, false> {};
|
||||
template<typename T, typename Allocator>
|
||||
struct ContainerTraits<std::deque<T, Allocator>>
|
||||
: public StdContainer<std::deque<T, Allocator>, true, false> {};
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -24,23 +24,42 @@
|
||||
#ifndef BITSERY_TRAITS_STD_FORWARD_LIST_H
|
||||
#define BITSERY_TRAITS_STD_FORWARD_LIST_H
|
||||
|
||||
#include "core/traits.h"
|
||||
#include "../details/serialization_common.h"
|
||||
#include <forward_list>
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
namespace traits {
|
||||
|
||||
template<typename ... TArgs>
|
||||
struct ContainerTraits<std::forward_list<TArgs...>> {
|
||||
using TValue = typename std::forward_list<TArgs...>::value_type;
|
||||
template<typename T, typename Allocator>
|
||||
struct ContainerTraits<std::forward_list<T, Allocator>> {
|
||||
using TValue = T;
|
||||
static constexpr bool isResizable = true;
|
||||
static constexpr bool isContiguous = false;
|
||||
static size_t size(const std::forward_list<TArgs...>& container) {
|
||||
static size_t size(const std::forward_list<T, Allocator>& container) {
|
||||
return static_cast<size_t>(std::distance(container.begin(), container.end()));
|
||||
}
|
||||
static void resize(std::forward_list<TArgs...>& container, size_t size) {
|
||||
static void resize(std::forward_list<T, Allocator>& container, size_t size) {
|
||||
resizeImpl(container, size, std::is_default_constructible<TValue>{});
|
||||
}
|
||||
private:
|
||||
static void resizeImpl(std::forward_list<T, Allocator>& container, size_t size, std::true_type) {
|
||||
container.resize(size);
|
||||
}
|
||||
static void resizeImpl(std::forward_list<T, Allocator>& container, size_t newSize, std::false_type) {
|
||||
const auto oldSize = size(container);
|
||||
for (auto it = oldSize; it < newSize; ++it) {
|
||||
container.push_front(::bitsery::Access::create<TValue>());
|
||||
}
|
||||
if (oldSize > newSize) {
|
||||
//erase_after must have atleast one element to work
|
||||
if (newSize > 0)
|
||||
container.erase_after(std::next(std::begin(container), newSize-1));
|
||||
else
|
||||
container.clear();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,9 +31,9 @@ namespace bitsery {
|
||||
|
||||
namespace traits {
|
||||
|
||||
template<typename ... TArgs>
|
||||
struct ContainerTraits<std::list<TArgs...>>
|
||||
: public StdContainer<std::list<TArgs...>, true, false> {};
|
||||
template<typename T, typename Allocator>
|
||||
struct ContainerTraits<std::list<T, Allocator>>
|
||||
: public StdContainer<std::list<T, Allocator>, true, false> {};
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -33,18 +33,18 @@ namespace bitsery {
|
||||
|
||||
// specialization for string, because string is already included for std::char_traits
|
||||
|
||||
template<typename ... TArgs>
|
||||
struct ContainerTraits<std::basic_string<TArgs...>>
|
||||
:public StdContainer<std::basic_string<TArgs...>, true, true> {};
|
||||
template<typename CharT, typename Traits, typename Allocator>
|
||||
struct ContainerTraits<std::basic_string<CharT, Traits, Allocator>>
|
||||
:public StdContainer<std::basic_string<CharT, Traits, Allocator>, true, true> {};
|
||||
|
||||
template <typename ... TArgs>
|
||||
struct TextTraits<std::basic_string<TArgs...>> {
|
||||
using TValue = typename ContainerTraits<std::basic_string<TArgs...>>::TValue;
|
||||
template <typename CharT, typename Traits, typename Allocator>
|
||||
struct TextTraits<std::basic_string<CharT, Traits, Allocator>> {
|
||||
using TValue = typename ContainerTraits<std::basic_string<CharT, Traits, Allocator>>::TValue;
|
||||
//string is automatically null-terminated
|
||||
static constexpr bool addNUL = false;
|
||||
|
||||
//is is not 100% accurate, but for performance reasons assume that string stores text, not binary data
|
||||
static size_t length(const std::basic_string<TArgs...>& str) {
|
||||
static size_t length(const std::basic_string<CharT, Traits, Allocator>& str) {
|
||||
return str.size();
|
||||
}
|
||||
};
|
||||
@@ -60,9 +60,9 @@ namespace bitsery {
|
||||
}
|
||||
};
|
||||
|
||||
template<typename ... TArgs>
|
||||
struct BufferAdapterTraits<std::basic_string<TArgs...>>
|
||||
:public StdContainerForBufferAdapter<std::basic_string<TArgs...>> {};
|
||||
template<typename CharT, typename Traits, typename Allocator>
|
||||
struct BufferAdapterTraits<std::basic_string<CharT, Traits, Allocator>>
|
||||
:public StdContainerForBufferAdapter<std::basic_string<CharT, Traits, Allocator>> {};
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -30,18 +30,18 @@
|
||||
namespace bitsery {
|
||||
|
||||
namespace traits {
|
||||
template<typename ... TArgs>
|
||||
struct ContainerTraits<std::vector<TArgs...>>
|
||||
:public StdContainer<std::vector<TArgs...>, true, true> {};
|
||||
template<typename T, typename Allocator>
|
||||
struct ContainerTraits<std::vector<T, Allocator>>
|
||||
:public StdContainer<std::vector<T, Allocator>, true, true> {};
|
||||
|
||||
//bool vector is not contiguous, do not copy it directly to buffer
|
||||
template<typename Allocator>
|
||||
struct ContainerTraits<std::vector<bool, Allocator>>
|
||||
:public StdContainer<std::vector<bool, Allocator>, true, false> {};
|
||||
|
||||
template<typename ... TArgs>
|
||||
struct BufferAdapterTraits<std::vector<TArgs...>>
|
||||
:public StdContainerForBufferAdapter<std::vector<TArgs...>> {};
|
||||
template<typename T, typename Allocator>
|
||||
struct BufferAdapterTraits<std::vector<T, Allocator>>
|
||||
:public StdContainerForBufferAdapter<std::vector<T, Allocator>> {};
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ set(CTEST_BINARY_DIRECTORY "build")
|
||||
set(ENV{CXXFLAGS} "--coverage")
|
||||
#when using Ninja generator, ctest_coverage cannot find files...
|
||||
set(CTEST_CMAKE_GENERATOR "CodeBlocks - Unix Makefiles")
|
||||
set(CTEST_USE_LAUNCHERS 1)
|
||||
#set(CTEST_USE_LAUNCHERS 1)
|
||||
|
||||
set(CTEST_COVERAGE_COMMAND "gcov")
|
||||
|
||||
|
||||
2
scripts/show_coverage.sh
Normal file → Executable file
2
scripts/show_coverage.sh
Normal file → Executable file
@@ -1,5 +1,5 @@
|
||||
#!/bin/sh
|
||||
BUILD_DIR=$1
|
||||
BUILD_DIR=./build
|
||||
TESTS_BUILD_DIR=$BUILD_DIR/tests/CMakeFiles/
|
||||
COV_INFO=$TESTS_BUILD_DIR/bitsery_coverage.info
|
||||
lcov --directory $TESTS_BUILD_DIR --capture --output-file $COV_INFO
|
||||
|
||||
@@ -38,11 +38,15 @@ endif()
|
||||
|
||||
enable_testing()
|
||||
|
||||
foreach(TestFile ${TestSourceFiles})
|
||||
foreach (TestFile ${TestSourceFiles})
|
||||
get_filename_component(TestName ${TestFile} NAME_WE)
|
||||
set(TestName bitsery.test.${TestName})
|
||||
add_executable(${TestName} ${TestFile})
|
||||
target_link_libraries(${TestName} PRIVATE GTest::Main Bitsery::bitsery)
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
|
||||
target_compile_options(${TestName} PRIVATE -Wextra -Wno-missing-braces -Wpedantic -Weffc++ -Wno-c++14-extensions)
|
||||
endif()
|
||||
|
||||
add_test(NAME ${TestName} COMMAND $<TARGET_FILE:${TestName}>)
|
||||
endforeach()
|
||||
|
||||
@@ -55,7 +59,7 @@ if (ParentDir)
|
||||
file(GLOB_RECURSE HeadersForIDE ${ParentDir}/include/bitsery/*.h)
|
||||
# create dummy target IDE
|
||||
file(WRITE ${CMAKE_BINARY_DIR}/dummy_for_ide.cpp "//generated by CMake to create dummy target with all includes for IDE.")
|
||||
add_library(bitsery.dummy_for_ide ${CMAKE_BINARY_DIR}/dummy_for_ide.cpp )
|
||||
add_library(bitsery.dummy_for_ide ${CMAKE_BINARY_DIR}/dummy_for_ide.cpp)
|
||||
# add headers so IDE correctly show them
|
||||
target_sources(bitsery.dummy_for_ide PRIVATE ${HeadersForIDE} serialization_test_utils.h)
|
||||
target_link_libraries(bitsery.dummy_for_ide PRIVATE GTest::Main Bitsery::bitsery)
|
||||
|
||||
@@ -20,13 +20,14 @@
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
|
||||
#include <bitsery/adapter/stream.h>
|
||||
#include <bitsery/adapter_writer.h>
|
||||
#include <bitsery/adapter_reader.h>
|
||||
#include <bitsery/traits/vector.h>
|
||||
#include <bitsery/traits/array.h>
|
||||
#include <bitsery/traits/string.h>
|
||||
#include <gmock/gmock.h>
|
||||
#include <sstream>
|
||||
|
||||
//some helper types
|
||||
|
||||
@@ -21,11 +21,11 @@
|
||||
//SOFTWARE.
|
||||
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <bitsery/adapter_writer.h>
|
||||
#include <bitsery/adapter_reader.h>
|
||||
#include <bitsery/ext/value_range.h>
|
||||
#include "serialization_test_utils.h"
|
||||
#include <gmock/gmock.h>
|
||||
|
||||
using testing::Eq;
|
||||
using testing::ContainerEq;
|
||||
@@ -56,19 +56,19 @@ using InverseReader = bitsery::AdapterReader<InputAdapter, InverseEndiannessConf
|
||||
TEST(DataEndianness, WhenWriteBytesThenBytesAreSwapped) {
|
||||
//fill initial values
|
||||
IntegralTypes src{};
|
||||
src.a = static_cast<int64_t>(0x1122334455667788);
|
||||
src.b = 0xBBCCDDEE;
|
||||
src.c = static_cast<int16_t>(0xCCDD);
|
||||
src.d = static_cast<uint8_t>(0xDD);
|
||||
src.e = static_cast<int8_t>(0xEE);
|
||||
src.a = static_cast<int64_t>(0x1122334455667788u);
|
||||
src.b = 0xBBCCDDEEu;
|
||||
src.c = static_cast<int16_t>(0xCCDDu);
|
||||
src.d = static_cast<uint8_t>(0xDDu);
|
||||
src.e = static_cast<int8_t>(0xEEu);
|
||||
|
||||
//fill expected result after swap
|
||||
IntegralTypes resInv{};
|
||||
resInv.a = static_cast<int64_t>(0x8877665544332211);
|
||||
resInv.b = 0xEEDDCCBB;
|
||||
resInv.c = static_cast<int16_t>(0xDDCC);
|
||||
resInv.d = static_cast<uint8_t>(0xDD);
|
||||
resInv.e = static_cast<int8_t>(0xEE);
|
||||
resInv.a = static_cast<int64_t>(0x8877665544332211u);
|
||||
resInv.b = 0xEEDDCCBBu;
|
||||
resInv.c = static_cast<int16_t>(0xDDCCu);
|
||||
resInv.d = static_cast<uint8_t>(0xDDu);
|
||||
resInv.e = static_cast<int8_t>(0xEEu);
|
||||
|
||||
//create and write to buffer
|
||||
Buffer buf{};
|
||||
|
||||
@@ -21,9 +21,10 @@
|
||||
//SOFTWARE.
|
||||
|
||||
|
||||
#include <bitsery/ext/value_range.h>
|
||||
#include <gmock/gmock.h>
|
||||
#include "serialization_test_utils.h"
|
||||
#include <bitsery/ext/value_range.h>
|
||||
|
||||
|
||||
using testing::Eq;
|
||||
using testing::ContainerEq;
|
||||
|
||||
@@ -153,3 +153,28 @@ TEST(DataReading, WhenReaderHasErrorsAllOperationsReadsReturnZero) {
|
||||
EXPECT_THAT(r2[1], Eq(0u));
|
||||
EXPECT_THAT(r3, Eq(0u));
|
||||
}
|
||||
|
||||
TEST(DataReading, ConstBufferAllAdapters) {
|
||||
//create and write to buffer
|
||||
uint16_t data = 7549;
|
||||
Buffer bufWrite{};
|
||||
Writer bw{bufWrite};
|
||||
bw.writeBytes<2>(data);
|
||||
bw.flush();
|
||||
const Buffer buf{bufWrite};
|
||||
|
||||
//read from buffer
|
||||
using Adapter1 = bitsery::InputBufferAdapter<const Buffer>;
|
||||
using Adapter2 = bitsery::UnsafeInputBufferAdapter<const Buffer>;
|
||||
|
||||
bitsery::AdapterReader<Adapter1, bitsery::DefaultConfig> r1{Adapter1{buf.begin(), buf.end()}};
|
||||
bitsery::AdapterReader<Adapter2, bitsery::DefaultConfig> r2{Adapter2{buf.begin(), buf.end()}};
|
||||
|
||||
uint16_t res1{};
|
||||
r1.readBytes<2>(res1);
|
||||
|
||||
uint16_t res2{};
|
||||
r2.readBytes<2>(res2);
|
||||
EXPECT_THAT(res1, Eq(data));
|
||||
EXPECT_THAT(res2, Eq(data));
|
||||
}
|
||||
|
||||
@@ -20,9 +20,11 @@
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
|
||||
#include <bitsery/traits/string.h>
|
||||
#include <gmock/gmock.h>
|
||||
#include "serialization_test_utils.h"
|
||||
#include <bitsery/traits/string.h>
|
||||
|
||||
|
||||
using testing::Eq;
|
||||
using SessionsEnabledWriter = bitsery::AdapterWriter<OutputAdapter, SessionsEnabledConfig>;
|
||||
|
||||
@@ -20,11 +20,12 @@
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include "serialization_test_utils.h"
|
||||
#include <bitsery/details/serialization_common.h>
|
||||
#include <bitsery/traits/array.h>
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include "serialization_test_utils.h"
|
||||
|
||||
using testing::Eq;
|
||||
using testing::ContainerEq;
|
||||
using bitsery::EndiannessType;
|
||||
|
||||
@@ -21,10 +21,7 @@
|
||||
//SOFTWARE.
|
||||
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include "serialization_test_utils.h"
|
||||
#include <bitsery/flexible.h>
|
||||
|
||||
#include <bitsery/flexible/string.h>
|
||||
#include <bitsery/flexible/array.h>
|
||||
#include <bitsery/flexible/vector.h>
|
||||
@@ -37,6 +34,10 @@
|
||||
#include <bitsery/flexible/unordered_map.h>
|
||||
#include <bitsery/flexible/set.h>
|
||||
#include <bitsery/flexible/unordered_set.h>
|
||||
#include <bitsery/flexible/memory.h>
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include "serialization_test_utils.h"
|
||||
|
||||
using testing::Eq;
|
||||
|
||||
@@ -45,9 +46,9 @@ TEST(FlexibleSyntax, FundamentalTypesAndBool) {
|
||||
MyEnumClass te = MyEnumClass::E4;
|
||||
float tf = 485.042f;
|
||||
double td = -454184.48445;
|
||||
bool tb=true;
|
||||
bool tb = true;
|
||||
SerializationContext ctx{};
|
||||
ctx.createSerializer().archive(ti,te,tf,td,tb);
|
||||
ctx.createSerializer().archive(ti, te, tf, td, tb);
|
||||
|
||||
//result
|
||||
int ri{};
|
||||
@@ -55,7 +56,7 @@ TEST(FlexibleSyntax, FundamentalTypesAndBool) {
|
||||
float rf{};
|
||||
double rd{};
|
||||
bool rb{};
|
||||
ctx.createDeserializer().archive(ri,re,rf,rd,rb);
|
||||
ctx.createDeserializer().archive(ri, re, rf, rd, rb);
|
||||
|
||||
//test
|
||||
EXPECT_THAT(ri, Eq(ti));
|
||||
@@ -70,9 +71,9 @@ TEST(FlexibleSyntax, UseObjectFncInsteadOfValueN) {
|
||||
MyEnumClass te = MyEnumClass::E4;
|
||||
float tf = 485.042f;
|
||||
double td = -454184.48445;
|
||||
bool tb=true;
|
||||
bool tb = true;
|
||||
SerializationContext ctx;
|
||||
auto& ser = ctx.createSerializer();
|
||||
auto &ser = ctx.createSerializer();
|
||||
ser.object(ti);
|
||||
ser.object(te);
|
||||
ser.object(tf);
|
||||
@@ -85,7 +86,7 @@ TEST(FlexibleSyntax, UseObjectFncInsteadOfValueN) {
|
||||
float rf{};
|
||||
double rd{};
|
||||
bool rb{};
|
||||
auto& des = ctx.createDeserializer();
|
||||
auto &des = ctx.createDeserializer();
|
||||
des.object(ri);
|
||||
des.object(re);
|
||||
des.object(rf);
|
||||
@@ -105,9 +106,9 @@ TEST(FlexibleSyntax, MixDifferentSyntax) {
|
||||
MyEnumClass te = MyEnumClass::E4;
|
||||
float tf = 485.042f;
|
||||
double td = -454184.48445;
|
||||
bool tb=true;
|
||||
bool tb = true;
|
||||
SerializationContext ctx;
|
||||
auto& ser = ctx.createSerializer();
|
||||
auto &ser = ctx.createSerializer();
|
||||
ser.value<sizeof(ti)>(ti);
|
||||
ser.archive(te, tf, td);
|
||||
ser.object(tb);
|
||||
@@ -118,7 +119,7 @@ TEST(FlexibleSyntax, MixDifferentSyntax) {
|
||||
float rf{};
|
||||
double rd{};
|
||||
bool rb{};
|
||||
auto& des = ctx.createDeserializer();
|
||||
auto &des = ctx.createDeserializer();
|
||||
des.archive(ri, re, rf);
|
||||
des.value8b(rd);
|
||||
des.object(rb);
|
||||
@@ -131,28 +132,27 @@ TEST(FlexibleSyntax, MixDifferentSyntax) {
|
||||
EXPECT_THAT(rb, Eq(tb));
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
template<typename T>
|
||||
T procArchive(const T& testData) {
|
||||
SerializationContext ctx;
|
||||
ctx.createSerializer().archive(testData);
|
||||
T res;
|
||||
T res{};
|
||||
ctx.createDeserializer().archive(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template<typename T>
|
||||
T procArchiveWithMaxSize(const T& testData) {
|
||||
SerializationContext ctx;
|
||||
ctx.createSerializer().archive(bitsery::maxSize(testData, 100));
|
||||
T res;
|
||||
T res{};
|
||||
ctx.createDeserializer().archive(bitsery::maxSize(res, 100));
|
||||
return res;
|
||||
}
|
||||
|
||||
TEST(FlexibleSyntax, CStyleArrayForValueTypesAsContainer) {
|
||||
const int t1[3]{8748,-484,45};
|
||||
int r1[3]{0,0,0};
|
||||
const int t1[3]{8748, -484, 45};
|
||||
int r1[3]{0, 0, 0};
|
||||
|
||||
SerializationContext ctx;
|
||||
ctx.createSerializer().archive(bitsery::asContainer(t1));
|
||||
@@ -163,7 +163,7 @@ TEST(FlexibleSyntax, CStyleArrayForValueTypesAsContainer) {
|
||||
|
||||
TEST(FlexibleSyntax, CStyleArrayForIntegralTypesAsText) {
|
||||
const char t1[3]{"hi"};
|
||||
char r1[3]{0,0,0};
|
||||
char r1[3]{0, 0, 0};
|
||||
|
||||
SerializationContext ctx;
|
||||
ctx.createSerializer().archive(bitsery::asText(t1));
|
||||
@@ -183,7 +183,6 @@ TEST(FlexibleSyntax, CStyleArray) {
|
||||
EXPECT_THAT(r1, ::testing::ContainerEq(t1));
|
||||
}
|
||||
|
||||
|
||||
TEST(FlexibleSyntax, StdString) {
|
||||
std::string t1{"my nice string"};
|
||||
std::string t2{};
|
||||
@@ -195,56 +194,51 @@ TEST(FlexibleSyntax, StdString) {
|
||||
}
|
||||
|
||||
TEST(FlexibleSyntax, StdArray) {
|
||||
std::array<int, 3> t1{8748,-484,45};
|
||||
std::array<int, 3> t1{8748, -484, 45};
|
||||
std::array<int, 0> t2{};
|
||||
|
||||
EXPECT_THAT(procArchive(t1), Eq(t1));
|
||||
EXPECT_THAT(procArchive(t2), Eq(t2));
|
||||
|
||||
}
|
||||
|
||||
TEST(FlexibleSyntax, StdVector) {
|
||||
std::vector<int> t1{8748,-484,45};
|
||||
std::vector<float> t2{5.f,0.198f};
|
||||
std::vector<int> t1{8748, -484, 45};
|
||||
std::vector<float> t2{5.f, 0.198f};
|
||||
|
||||
EXPECT_THAT(procArchive(t1), Eq(t1));
|
||||
EXPECT_THAT(procArchive(t2), Eq(t2));
|
||||
EXPECT_THAT(procArchiveWithMaxSize(t1), Eq(t1));
|
||||
EXPECT_THAT(procArchiveWithMaxSize(t2), Eq(t2));
|
||||
|
||||
}
|
||||
|
||||
TEST(FlexibleSyntax, StdList) {
|
||||
std::list<int> t1{8748,-484,45};
|
||||
std::list<float> t2{5.f,0.198f};
|
||||
std::list<int> t1{8748, -484, 45};
|
||||
std::list<float> t2{5.f, 0.198f};
|
||||
|
||||
EXPECT_THAT(procArchive(t1), Eq(t1));
|
||||
EXPECT_THAT(procArchive(t2), Eq(t2));
|
||||
EXPECT_THAT(procArchiveWithMaxSize(t1), Eq(t1));
|
||||
EXPECT_THAT(procArchiveWithMaxSize(t2), Eq(t2));
|
||||
|
||||
}
|
||||
|
||||
TEST(FlexibleSyntax, StdForwardList) {
|
||||
std::forward_list<int> t1{8748,-484,45};
|
||||
std::forward_list<float> t2{5.f,0.198f};
|
||||
std::forward_list<int> t1{8748, -484, 45};
|
||||
std::forward_list<float> t2{5.f, 0.198f};
|
||||
|
||||
EXPECT_THAT(procArchive(t1), Eq(t1));
|
||||
EXPECT_THAT(procArchive(t2), Eq(t2));
|
||||
EXPECT_THAT(procArchiveWithMaxSize(t1), Eq(t1));
|
||||
EXPECT_THAT(procArchiveWithMaxSize(t2), Eq(t2));
|
||||
|
||||
}
|
||||
|
||||
TEST(FlexibleSyntax, StdDeque) {
|
||||
std::deque<int> t1{8748,-484,45};
|
||||
std::deque<float> t2{5.f,0.198f};
|
||||
std::deque<int> t1{8748, -484, 45};
|
||||
std::deque<float> t2{5.f, 0.198f};
|
||||
|
||||
EXPECT_THAT(procArchive(t1), Eq(t1));
|
||||
EXPECT_THAT(procArchive(t2), Eq(t2));
|
||||
EXPECT_THAT(procArchiveWithMaxSize(t1), Eq(t1));
|
||||
EXPECT_THAT(procArchiveWithMaxSize(t2), Eq(t2));
|
||||
|
||||
}
|
||||
|
||||
TEST(FlexibleSyntax, StdQueue) {
|
||||
@@ -254,7 +248,6 @@ TEST(FlexibleSyntax, StdQueue) {
|
||||
|
||||
EXPECT_THAT(procArchive(t1), Eq(t1));
|
||||
EXPECT_THAT(procArchiveWithMaxSize(t1), Eq(t1));
|
||||
|
||||
}
|
||||
|
||||
TEST(FlexibleSyntax, StdPriorityQueue) {
|
||||
@@ -272,7 +265,6 @@ TEST(FlexibleSyntax, StdPriorityQueue) {
|
||||
r1.pop();
|
||||
t1.pop();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
TEST(FlexibleSyntax, StdStack) {
|
||||
@@ -282,13 +274,12 @@ TEST(FlexibleSyntax, StdStack) {
|
||||
|
||||
EXPECT_THAT(procArchive(t1), Eq(t1));
|
||||
EXPECT_THAT(procArchiveWithMaxSize(t1), Eq(t1));
|
||||
|
||||
}
|
||||
|
||||
TEST(FlexibleSyntax, StdUnorderedMap) {
|
||||
std::unordered_map<int, int> t1;
|
||||
t1.emplace(3423,624);
|
||||
t1.emplace(-5484,-845);
|
||||
t1.emplace(3423, 624);
|
||||
t1.emplace(-5484, -845);
|
||||
|
||||
EXPECT_THAT(procArchive(t1), Eq(t1));
|
||||
EXPECT_THAT(procArchiveWithMaxSize(t1), Eq(t1));
|
||||
@@ -296,9 +287,9 @@ TEST(FlexibleSyntax, StdUnorderedMap) {
|
||||
|
||||
TEST(FlexibleSyntax, StdUnorderedMultiMap) {
|
||||
std::unordered_multimap<std::string, int> t1;
|
||||
t1.emplace("one",624);
|
||||
t1.emplace("two",-845);
|
||||
t1.emplace("one",897);
|
||||
t1.emplace("one", 624);
|
||||
t1.emplace("two", -845);
|
||||
t1.emplace("one", 897);
|
||||
|
||||
EXPECT_TRUE(procArchive(t1) == t1);
|
||||
EXPECT_TRUE(procArchiveWithMaxSize(t1) == t1);
|
||||
@@ -306,8 +297,8 @@ TEST(FlexibleSyntax, StdUnorderedMultiMap) {
|
||||
|
||||
TEST(FlexibleSyntax, StdMap) {
|
||||
std::map<int, int> t1;
|
||||
t1.emplace(3423,624);
|
||||
t1.emplace(-5484,-845);
|
||||
t1.emplace(3423, 624);
|
||||
t1.emplace(-5484, -845);
|
||||
|
||||
EXPECT_THAT(procArchive(t1), Eq(t1));
|
||||
EXPECT_THAT(procArchiveWithMaxSize(t1), Eq(t1));
|
||||
@@ -315,9 +306,9 @@ TEST(FlexibleSyntax, StdMap) {
|
||||
|
||||
TEST(FlexibleSyntax, StdMultiMap) {
|
||||
std::multimap<std::string, int> t1;
|
||||
t1.emplace("one",624);
|
||||
t1.emplace("two",-845);
|
||||
t1.emplace("one",897);
|
||||
t1.emplace("one", 624);
|
||||
t1.emplace("two", -845);
|
||||
t1.emplace("one", 897);
|
||||
|
||||
auto res = procArchive(t1);
|
||||
//same key values is not ordered, and operator == compares each element at same position
|
||||
@@ -375,6 +366,27 @@ TEST(FlexibleSyntax, StdMultiSet) {
|
||||
EXPECT_TRUE(procArchiveWithMaxSize(t1) == t1);
|
||||
}
|
||||
|
||||
TEST(FlexibleSyntax, StdSmartPtr) {
|
||||
std::shared_ptr<int> dataShared1(new int{4});
|
||||
std::weak_ptr<int> dataWeak1(dataShared1);
|
||||
std::unique_ptr<std::string> dataUnique1{new std::string{"hello world"}};
|
||||
|
||||
bitsery::ext::PointerLinkingContext plctx1{};
|
||||
BasicSerializationContext<bitsery::DefaultConfig, bitsery::ext::PointerLinkingContext> ctx;
|
||||
ctx.createSerializer(&plctx1).archive(dataShared1, dataWeak1, dataUnique1);
|
||||
|
||||
std::shared_ptr<int> resShared1{};
|
||||
std::weak_ptr<int> resWeak1{};
|
||||
std::unique_ptr<std::string> resUnique1{};
|
||||
ctx.createDeserializer(&plctx1).archive(resShared1, resWeak1, resUnique1);
|
||||
//clear shared state from pointer linking context
|
||||
plctx1.clearSharedState();
|
||||
|
||||
EXPECT_TRUE(plctx1.isValid());
|
||||
EXPECT_THAT(*resShared1, Eq(*dataShared1));
|
||||
EXPECT_THAT(*resWeak1.lock(), Eq(*dataWeak1.lock()));
|
||||
EXPECT_THAT(*resUnique1, Eq(*dataUnique1));
|
||||
}
|
||||
|
||||
TEST(FlexibleSyntax, NestedTypes) {
|
||||
std::unordered_map<std::string, std::vector<std::string>> t1;
|
||||
|
||||
328
tests/not_default_constructible.cpp
Normal file
328
tests/not_default_constructible.cpp
Normal file
@@ -0,0 +1,328 @@
|
||||
//MIT License
|
||||
//
|
||||
//Copyright (c) 2019 Mindaugas Vinkelis
|
||||
//
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
//of this software and associated documentation files (the "Software"), to deal
|
||||
//in the Software without restriction, including without limitation the rights
|
||||
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
//copies of the Software, and to permit persons to whom the Software is
|
||||
//furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//The above copyright notice and this permission notice shall be included in all
|
||||
//copies or substantial portions of the Software.
|
||||
//
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
#include <forward_list>
|
||||
#include <bitsery/traits/forward_list.h>
|
||||
#include <bitsery/ext/std_set.h>
|
||||
#include <bitsery/ext/std_map.h>
|
||||
#include <bitsery/ext/pointer.h>
|
||||
#include <bitsery/ext/std_smart_ptr.h>
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include "serialization_test_utils.h"
|
||||
|
||||
|
||||
using testing::ContainerEq;
|
||||
using testing::Eq;
|
||||
|
||||
//forward declare, for testing with std::unordered_map
|
||||
class HasherForNonDefaultConstructible;
|
||||
|
||||
class NonDefaultConstructible {
|
||||
int32_t i{0};
|
||||
friend class HasherForNonDefaultConstructible;
|
||||
|
||||
|
||||
|
||||
|
||||
friend class bitsery::Access;
|
||||
NonDefaultConstructible() = default;
|
||||
|
||||
template <typename S>
|
||||
void serialize(S& s) {
|
||||
s.value4b(i);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
explicit NonDefaultConstructible(int32_t v):i{v} {}
|
||||
|
||||
bool operator == (const NonDefaultConstructible& other) const {
|
||||
return i == other.i;
|
||||
}
|
||||
|
||||
bool operator < (const NonDefaultConstructible& other) const {
|
||||
return i < other.i;
|
||||
}
|
||||
};
|
||||
|
||||
class HasherForNonDefaultConstructible {
|
||||
public:
|
||||
size_t operator()(const NonDefaultConstructible& o) const {
|
||||
return std::hash<int32_t>()(o.i);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
TEST(DeserializeNonDefaultConstructible, Container) {
|
||||
SerializationContext ctx{};
|
||||
std::vector<NonDefaultConstructible> data{};
|
||||
data.emplace_back(1);
|
||||
data.emplace_back(2);
|
||||
data.emplace_back(3);
|
||||
std::vector<NonDefaultConstructible> res{};
|
||||
|
||||
ctx.createSerializer().container(data, 10);
|
||||
ctx.createDeserializer().container(res, 10);
|
||||
|
||||
EXPECT_THAT(res, ContainerEq(data));
|
||||
}
|
||||
//this test is here, because when object is not constructible we cannot simple "resize" container
|
||||
TEST(DeserializeNonDefaultConstructible, ResultContainerShouldShrink) {
|
||||
SerializationContext ctx{};
|
||||
std::vector<NonDefaultConstructible> data{};
|
||||
data.emplace_back(1);
|
||||
std::vector<NonDefaultConstructible> res{};
|
||||
res.emplace_back(2);
|
||||
res.emplace_back(3);
|
||||
|
||||
ctx.createSerializer().container(data, 10);
|
||||
ctx.createDeserializer().container(res, 10);
|
||||
|
||||
EXPECT_THAT(res, ContainerEq(data));
|
||||
}
|
||||
|
||||
TEST(DeserializeNonDefaultConstructible, ResultStdForwardListShouldShrink) {
|
||||
// forward list doesn't have .erase function, bet has erase_after
|
||||
// in this case, if new size is 0 it must call clear, so we need to check two cases
|
||||
{
|
||||
// 1) when result should have more than 0 elements
|
||||
SerializationContext ctx{};
|
||||
std::forward_list<NonDefaultConstructible> data{};
|
||||
data.push_front(NonDefaultConstructible{1});
|
||||
std::forward_list<NonDefaultConstructible> res{};
|
||||
res.push_front(NonDefaultConstructible{21});
|
||||
res.push_front(NonDefaultConstructible{14});
|
||||
|
||||
ctx.createSerializer().container(data, 10);
|
||||
ctx.createDeserializer().container(res, 10);
|
||||
|
||||
auto resIt = res.begin();
|
||||
for (auto it = data.begin(); it != data.end(); ++it, ++resIt) {
|
||||
EXPECT_THAT(*resIt, Eq(*it));
|
||||
}
|
||||
EXPECT_THAT(resIt, Eq(res.end()));
|
||||
|
||||
}
|
||||
{
|
||||
// 1) when result should have 0 elements
|
||||
SerializationContext ctx{};
|
||||
std::forward_list<NonDefaultConstructible> data{};
|
||||
std::forward_list<NonDefaultConstructible> res{};
|
||||
res.push_front(NonDefaultConstructible{1});
|
||||
res.push_front(NonDefaultConstructible{14});
|
||||
|
||||
ctx.createSerializer().container(data, 10);
|
||||
ctx.createDeserializer().container(res, 10);
|
||||
|
||||
EXPECT_THAT(res.begin(), Eq(res.end()));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
TEST(DeserializeNonDefaultConstructible, StdSet) {
|
||||
SerializationContext ctx{};
|
||||
std::set<NonDefaultConstructible> data;
|
||||
data.insert(NonDefaultConstructible{1});
|
||||
data.insert(NonDefaultConstructible{2});
|
||||
std::set<NonDefaultConstructible> res{};
|
||||
data.insert(NonDefaultConstructible{3});
|
||||
|
||||
ctx.createSerializer().ext(data, bitsery::ext::StdSet{10});
|
||||
ctx.createDeserializer().ext(res, bitsery::ext::StdSet{10});
|
||||
|
||||
EXPECT_THAT(res, ContainerEq(data));
|
||||
}
|
||||
|
||||
TEST(DeserializeNonDefaultConstructible, StdMap) {
|
||||
SerializationContext ctx{};
|
||||
std::unordered_map<NonDefaultConstructible, NonDefaultConstructible, HasherForNonDefaultConstructible> data;
|
||||
data.emplace(NonDefaultConstructible{2}, NonDefaultConstructible{3});
|
||||
|
||||
std::unordered_map<NonDefaultConstructible, NonDefaultConstructible, HasherForNonDefaultConstructible> res{};
|
||||
data.emplace(NonDefaultConstructible{2}, NonDefaultConstructible{3});
|
||||
data.emplace(NonDefaultConstructible{4}, NonDefaultConstructible{4});
|
||||
|
||||
auto& ser = ctx.createSerializer();
|
||||
ser.ext(data, bitsery::ext::StdMap{10},[&ser](NonDefaultConstructible& key, NonDefaultConstructible& value) {
|
||||
ser.object(key);
|
||||
ser.object(value);
|
||||
});
|
||||
auto& des = ctx.createDeserializer();
|
||||
des.ext(res, bitsery::ext::StdMap{10},[&des](NonDefaultConstructible& key, NonDefaultConstructible& value) {
|
||||
des.object(key);
|
||||
des.object(value);
|
||||
});
|
||||
|
||||
EXPECT_THAT(res, ContainerEq(data));
|
||||
}
|
||||
|
||||
|
||||
struct NonPolymorphicPointers {
|
||||
NonDefaultConstructible* pp;
|
||||
std::unique_ptr<NonDefaultConstructible> up;
|
||||
std::shared_ptr<NonDefaultConstructible> sp;
|
||||
std::weak_ptr<NonDefaultConstructible> wp;
|
||||
};
|
||||
|
||||
template <typename S>
|
||||
void serialize(S& s, NonPolymorphicPointers& o) {
|
||||
s.ext(o.pp, bitsery::ext::PointerOwner{});
|
||||
s.ext(o.up, bitsery::ext::StdSmartPtr{});
|
||||
s.ext(o.sp, bitsery::ext::StdSmartPtr{});
|
||||
s.ext(o.wp, bitsery::ext::StdSmartPtr{});
|
||||
}
|
||||
|
||||
TEST(DeserializeNonDefaultConstructible, NonPolymorphicPointerAndSmartPointer) {
|
||||
using SerContext = BasicSerializationContext<bitsery::DefaultConfig, bitsery::ext::PointerLinkingContext>;
|
||||
SerContext ctx{};
|
||||
NonPolymorphicPointers data{};
|
||||
data.pp = new NonDefaultConstructible{3};
|
||||
data.up = std::unique_ptr<NonDefaultConstructible>(new NonDefaultConstructible{54});
|
||||
data.sp = std::shared_ptr<NonDefaultConstructible>(new NonDefaultConstructible{-481});
|
||||
data.wp = data.sp;
|
||||
|
||||
NonPolymorphicPointers res{};
|
||||
bitsery::ext::PointerLinkingContext plctx1{};
|
||||
ctx.createSerializer(&plctx1).object(data);
|
||||
ctx.createDeserializer(&plctx1).object(res);
|
||||
|
||||
EXPECT_THAT(*res.pp, Eq(*data.pp));
|
||||
delete res.pp;
|
||||
delete data.pp;
|
||||
EXPECT_THAT(*res.up, Eq(*data.up));
|
||||
EXPECT_THAT(*res.sp, Eq(*data.sp));
|
||||
EXPECT_THAT(*(res.wp.lock()), Eq(*(data.wp.lock())));
|
||||
}
|
||||
|
||||
class PolymorphicNDCBase {
|
||||
public:
|
||||
virtual ~PolymorphicNDCBase() = 0;
|
||||
template <typename S>
|
||||
void serialize(S& ) {}
|
||||
};
|
||||
|
||||
|
||||
PolymorphicNDCBase::~PolymorphicNDCBase() = default;
|
||||
|
||||
class PolymorphicNDC1:public PolymorphicNDCBase {
|
||||
int8_t i{};
|
||||
friend class bitsery::Access;
|
||||
|
||||
template <typename S>
|
||||
void serialize(S& s) {
|
||||
s.value1b(i);
|
||||
}
|
||||
public:
|
||||
PolymorphicNDC1() = default;
|
||||
PolymorphicNDC1(int8_t v):i{v} {}
|
||||
bool operator == (const PolymorphicNDC1& other) const {
|
||||
return i == other.i;
|
||||
}
|
||||
};
|
||||
|
||||
class PolymorphicNDC2:public PolymorphicNDCBase {
|
||||
uint16_t ui{};
|
||||
|
||||
friend class bitsery::Access;
|
||||
|
||||
template <typename S>
|
||||
void serialize(S& s) {
|
||||
s.value2b(ui);
|
||||
}
|
||||
public:
|
||||
PolymorphicNDC2() = default;
|
||||
PolymorphicNDC2(uint16_t v):ui{v} {}
|
||||
bool operator == (const PolymorphicNDC2& other) const {
|
||||
return ui == other.ui;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
namespace bitsery {
|
||||
namespace ext {
|
||||
|
||||
template<>
|
||||
struct PolymorphicBaseClass<PolymorphicNDCBase> : PolymorphicDerivedClasses<PolymorphicNDC1, PolymorphicNDC2> {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct PolymorphicPointers {
|
||||
PolymorphicNDCBase* pp;
|
||||
std::unique_ptr<PolymorphicNDCBase> up;
|
||||
std::shared_ptr<PolymorphicNDCBase> sp;
|
||||
std::weak_ptr<PolymorphicNDCBase> wp;
|
||||
};
|
||||
|
||||
template <typename S>
|
||||
void serialize(S& s, PolymorphicPointers& o) {
|
||||
s.ext(o.pp, bitsery::ext::PointerOwner{});
|
||||
s.ext(o.up, bitsery::ext::StdSmartPtr{});
|
||||
s.ext(o.sp, bitsery::ext::StdSmartPtr{});
|
||||
s.ext(o.wp, bitsery::ext::StdSmartPtr{});
|
||||
}
|
||||
|
||||
TEST(DeserializeNonDefaultConstructible, PolymorphicPointerAndSmartPointer) {
|
||||
using TContext = std::tuple<bitsery::ext::PointerLinkingContext, bitsery::ext::PolymorphicContext<bitsery::ext::StandardRTTI>>;
|
||||
using SerContext = BasicSerializationContext<bitsery::DefaultConfig, TContext>;
|
||||
SerContext ctx{};
|
||||
PolymorphicPointers data{};
|
||||
data.pp = new PolymorphicNDC1{-4};
|
||||
data.up = std::unique_ptr<PolymorphicNDCBase>(new PolymorphicNDC2{54});
|
||||
data.sp = std::shared_ptr<PolymorphicNDCBase>(new PolymorphicNDC1{15});
|
||||
data.wp = data.sp;
|
||||
|
||||
PolymorphicPointers res{};
|
||||
TContext serCtx{};
|
||||
std::get<1>(serCtx).registerBasesList<typename SerContext::TSerializer>(bitsery::ext::PolymorphicClassesList<PolymorphicNDCBase>{});
|
||||
TContext desCtx{};
|
||||
std::get<1>(desCtx).registerBasesList<typename SerContext::TDeserializer>(bitsery::ext::PolymorphicClassesList<PolymorphicNDCBase>{});
|
||||
|
||||
ctx.createSerializer(&serCtx).object(data);
|
||||
ctx.createDeserializer(&desCtx).object(res);
|
||||
auto respp = dynamic_cast<PolymorphicNDC1*>(res.pp);
|
||||
auto resup = dynamic_cast<PolymorphicNDC2*>(res.up.get());
|
||||
auto ressp = dynamic_cast<PolymorphicNDC1*>(res.sp.get());
|
||||
auto reswp = dynamic_cast<PolymorphicNDC1*>(res.wp.lock().get());
|
||||
|
||||
auto datapp = dynamic_cast<PolymorphicNDC1*>(data.pp);
|
||||
auto dataup = dynamic_cast<PolymorphicNDC2*>(data.up.get());
|
||||
auto datasp = dynamic_cast<PolymorphicNDC1*>(data.sp.get());
|
||||
auto datawp = dynamic_cast<PolymorphicNDC1*>(data.wp.lock().get());
|
||||
|
||||
EXPECT_THAT(respp, ::testing::Ne(nullptr));
|
||||
EXPECT_THAT(resup, ::testing::Ne(nullptr));
|
||||
EXPECT_THAT(ressp, ::testing::Ne(nullptr));
|
||||
EXPECT_THAT(reswp, ::testing::Ne(nullptr));
|
||||
|
||||
EXPECT_THAT(*respp, Eq(*datapp));
|
||||
delete res.pp;
|
||||
delete data.pp;
|
||||
EXPECT_THAT(*resup, Eq(*dataup));
|
||||
EXPECT_THAT(*ressp, Eq(*datasp));
|
||||
EXPECT_THAT(*reswp, Eq(*datawp));
|
||||
}
|
||||
@@ -22,16 +22,14 @@
|
||||
|
||||
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <algorithm>
|
||||
#include <numeric>
|
||||
|
||||
#include "serialization_test_utils.h"
|
||||
#include <bitsery/traits/array.h>
|
||||
#include <bitsery/traits/list.h>
|
||||
#include <bitsery/traits/deque.h>
|
||||
#include <bitsery/traits/forward_list.h>
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include "serialization_test_utils.h"
|
||||
|
||||
using testing::ContainerEq;
|
||||
using testing::Eq;
|
||||
|
||||
@@ -266,4 +264,6 @@ TEST_P(SerializeContainer, SizeHasVariableLength) {
|
||||
EXPECT_THAT(ctx.getBufferSize(), Eq(ctx.containerSizeSerializedBytesCount(src.size())));
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(LargeContainerSize, SerializeContainer, ::testing::Values(0x01, 0x80, 0x4000));
|
||||
//last comma is to suppress error that otherwise can be suppressed by clang/gcc with -Wgnu-zero-variadic-macro-arguments
|
||||
INSTANTIATE_TEST_CASE_P(LargeContainerSize, SerializeContainer, ::testing::Values(0x01, 0x80, 0x4000),);
|
||||
|
||||
|
||||
241
tests/serialization_ext_compact_value.cpp
Normal file
241
tests/serialization_ext_compact_value.cpp
Normal file
@@ -0,0 +1,241 @@
|
||||
//MIT License
|
||||
//
|
||||
//Copyright (c) 2017 Mindaugas Vinkelis
|
||||
//
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
//of this software and associated documentation files (the "Software"), to deal
|
||||
//in the Software without restriction, including without limitation the rights
|
||||
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
//copies of the Software, and to permit persons to whom the Software is
|
||||
//furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//The above copyright notice and this permission notice shall be included in all
|
||||
//copies or substantial portions of the Software.
|
||||
//
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
|
||||
#include <bitsery/ext/compact_value.h>
|
||||
#include <gmock/gmock.h>
|
||||
#include "serialization_test_utils.h"
|
||||
|
||||
#include <bitsery/traits/array.h>
|
||||
#include <iostream>
|
||||
#include <bitset>
|
||||
#include <chrono>
|
||||
|
||||
using testing::Eq;
|
||||
using bitsery::ext::CompactValue;
|
||||
using bitsery::ext::CompactValueAsObject;
|
||||
using bitsery::EndiannessType;
|
||||
|
||||
// helper function, that gets value filled with specified number of bits
|
||||
template <typename TValue>
|
||||
TValue getValue(bool isPositive, size_t significantBits) {
|
||||
TValue v = isPositive ? 0 : -1;
|
||||
if (significantBits == 0)
|
||||
return v;
|
||||
|
||||
using TUnsigned = typename std::make_unsigned<TValue>::type;
|
||||
TUnsigned mask = {};
|
||||
mask = ~mask; // invert shiftByBits
|
||||
auto shiftBy = bitsery::details::BitsSize<TValue>::value - significantBits;
|
||||
mask >>= shiftBy;
|
||||
//cast to unsigned when applying mask
|
||||
return (TUnsigned)v ^ mask;
|
||||
}
|
||||
|
||||
// helper function, that serialize and return deserialized value
|
||||
template <typename TSerContext, typename TValue>
|
||||
std::pair<TValue, size_t> serializeAndGetDeserialized(TValue data) {
|
||||
TSerContext ctx;
|
||||
TValue res{};
|
||||
ctx.createSerializer().template ext<sizeof(TValue)>(data, CompactValue{});
|
||||
ctx.createDeserializer().template ext<sizeof(TValue)>(res, CompactValue{});
|
||||
return {res, ctx.getBufferSize()};
|
||||
}
|
||||
|
||||
struct LittleEndianConfig: public bitsery::DefaultConfig {
|
||||
static constexpr EndiannessType NetworkEndianness = EndiannessType::LittleEndian;
|
||||
};
|
||||
|
||||
struct BigEndianConfig: public bitsery::DefaultConfig {
|
||||
static constexpr EndiannessType NetworkEndianness = EndiannessType::BigEndian;
|
||||
};
|
||||
|
||||
template <typename TValue, bool isPositiveNr, typename TConfig>
|
||||
struct TC {
|
||||
static_assert(isPositiveNr || std::is_signed<TValue>::value, "");
|
||||
|
||||
using Value = TValue;
|
||||
using Config = TConfig;
|
||||
bool isPositive = isPositiveNr;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class SerializeExtensionCompactValueCorrectness : public testing::Test {
|
||||
public:
|
||||
using TestCase = T;
|
||||
};
|
||||
|
||||
|
||||
using AllValueSizesTestCases = ::testing::Types<
|
||||
TC<uint8_t, true, LittleEndianConfig>,
|
||||
TC<uint16_t, true, LittleEndianConfig>,
|
||||
TC<uint32_t, true, LittleEndianConfig>,
|
||||
TC<uint64_t, true, LittleEndianConfig>,
|
||||
TC<int8_t, true, LittleEndianConfig>,
|
||||
TC<int16_t, true, LittleEndianConfig>,
|
||||
TC<int32_t, true, LittleEndianConfig>,
|
||||
TC<int64_t, true, LittleEndianConfig>,
|
||||
TC<int8_t, false, LittleEndianConfig>,
|
||||
TC<int16_t, false, LittleEndianConfig>,
|
||||
TC<int32_t, false, LittleEndianConfig>,
|
||||
TC<int64_t, false, LittleEndianConfig>,
|
||||
TC<uint8_t, true, BigEndianConfig>,
|
||||
TC<uint16_t, true, BigEndianConfig>,
|
||||
TC<uint32_t, true, BigEndianConfig>,
|
||||
TC<uint64_t, true, BigEndianConfig>,
|
||||
TC<int8_t, true, BigEndianConfig>,
|
||||
TC<int16_t, true, BigEndianConfig>,
|
||||
TC<int32_t, true, BigEndianConfig>,
|
||||
TC<int64_t, true, BigEndianConfig>,
|
||||
TC<int8_t, false, BigEndianConfig>,
|
||||
TC<int16_t, false, BigEndianConfig>,
|
||||
TC<int32_t, false, BigEndianConfig>,
|
||||
TC<int64_t, false, BigEndianConfig>
|
||||
>;
|
||||
|
||||
TYPED_TEST_CASE(SerializeExtensionCompactValueCorrectness, AllValueSizesTestCases);
|
||||
|
||||
TYPED_TEST(SerializeExtensionCompactValueCorrectness, TestDifferentSizeValues) {
|
||||
using TCase = typename TestFixture::TestCase;
|
||||
using TValue = typename TCase::Value;
|
||||
TCase tc{};
|
||||
|
||||
for (auto i = 0u; i < bitsery::details::BitsSize<TValue>::value + 1; ++i) {
|
||||
auto data = getValue<TValue>(tc.isPositive, i);
|
||||
auto res = serializeAndGetDeserialized<BasicSerializationContext<typename TCase::Config, void>>(data);
|
||||
EXPECT_THAT(res.first, Eq(data));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// this stucture will contain test data and result, as type paramters
|
||||
template <typename TValue, bool isPositiveNr, size_t significantBits, size_t resultBytes>
|
||||
struct SizeTC {
|
||||
static_assert(isPositiveNr || std::is_signed<TValue>::value, "");
|
||||
static_assert(bitsery::details::BitsSize<TValue>::value >= significantBits, "");
|
||||
|
||||
using Value = TValue;
|
||||
bool isPositive = isPositiveNr;
|
||||
size_t fillBits = significantBits;
|
||||
size_t bytesCount = resultBytes;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class SerializeExtensionCompactValueRequiredBytes : public testing::Test {
|
||||
public:
|
||||
using TestCase = T;
|
||||
};
|
||||
|
||||
using RequiredBytesTestCases = ::testing::Types<
|
||||
//1 byte always writes to 1 byte
|
||||
SizeTC<uint8_t, true, 0,1>,
|
||||
SizeTC<uint8_t, true, 8,1>,
|
||||
SizeTC<int8_t, false, 0,1>,
|
||||
SizeTC<int8_t, true, 8,1>,
|
||||
|
||||
//2 byte, +1 byte after 15 significant bits
|
||||
SizeTC<uint16_t, true, 7,1>,
|
||||
SizeTC<uint16_t, true, 8,2>,
|
||||
SizeTC<uint16_t, true, 14,2>,
|
||||
SizeTC<uint16_t, true, 15,3>,
|
||||
//2 byte, +1 byte after 15-1 significant bits (1 bit for sign)
|
||||
SizeTC<int16_t, true, 6,1>,
|
||||
SizeTC<int16_t, false, 7,2>,
|
||||
SizeTC<int16_t, true, 13,2>,
|
||||
SizeTC<int16_t, false, 14,3>,
|
||||
|
||||
//4 byte, +1 byte after 29 significant bits
|
||||
SizeTC<uint32_t, true, 14,2>,
|
||||
SizeTC<uint32_t, true, 21,3>,
|
||||
SizeTC<uint32_t, true, 28,4>,
|
||||
SizeTC<uint32_t, true, 29,5>,
|
||||
SizeTC<uint32_t, true, 32,5>,
|
||||
//4 byte
|
||||
SizeTC<int32_t, true, 13,2>,
|
||||
SizeTC<int32_t, false, 20,3>,
|
||||
SizeTC<int32_t, true, 27,4>,
|
||||
SizeTC<int32_t, false, 28,5>,
|
||||
SizeTC<int32_t, true, 31,5>,
|
||||
|
||||
//8 byte, +1 byte after 57 significant bits, or +2 byte when all bits are significant
|
||||
SizeTC<uint64_t, true, 28,4>,
|
||||
SizeTC<uint64_t, true, 35,5>,
|
||||
SizeTC<uint64_t, true, 42,6>,
|
||||
SizeTC<uint64_t, true, 49,7>,
|
||||
SizeTC<uint64_t, true, 56,8>,
|
||||
SizeTC<uint64_t, true, 57,9>,
|
||||
SizeTC<uint64_t, true, 63,9>,
|
||||
SizeTC<uint64_t, true, 64,10>,
|
||||
//8 byte,
|
||||
SizeTC<int64_t, true, 27,4>,
|
||||
SizeTC<int64_t, false, 34,5>,
|
||||
SizeTC<int64_t, true, 41,6>,
|
||||
SizeTC<int64_t, false, 48,7>,
|
||||
SizeTC<int64_t, true, 55,8>,
|
||||
SizeTC<int64_t, false, 56,9>,
|
||||
SizeTC<int64_t, true, 62,9>,
|
||||
SizeTC<int64_t, false, 63,10>
|
||||
>;
|
||||
|
||||
TYPED_TEST_CASE(SerializeExtensionCompactValueRequiredBytes, RequiredBytesTestCases);
|
||||
|
||||
TYPED_TEST(SerializeExtensionCompactValueRequiredBytes, Test) {
|
||||
using TCase = typename TestFixture::TestCase;
|
||||
using TValue = typename TCase::Value;
|
||||
TCase tc{};
|
||||
TValue data = getValue<TValue>(tc.isPositive, tc.fillBits);
|
||||
auto res = serializeAndGetDeserialized<SerializationContext>(data);
|
||||
EXPECT_THAT(res.first, Eq(data));
|
||||
EXPECT_THAT(res.second, tc.bytesCount);
|
||||
}
|
||||
|
||||
enum b1En: uint8_t {
|
||||
A,B,C,D=54,E
|
||||
};
|
||||
|
||||
enum class b8En: int64_t {
|
||||
A=-874987489,B,C=0,D,E=489748978, F,G
|
||||
};
|
||||
|
||||
TEST(SerializeExtensionCompactValueEnum, TestEnums) {
|
||||
auto d1 = b1En::E;
|
||||
auto d2 = b8En::B;
|
||||
auto d3 = b8En::F;
|
||||
EXPECT_THAT(serializeAndGetDeserialized<SerializationContext>(d1).first, Eq(d1));
|
||||
EXPECT_THAT(serializeAndGetDeserialized<SerializationContext>(d2).first, Eq(d2));
|
||||
EXPECT_THAT(serializeAndGetDeserialized<SerializationContext>(d3).first, Eq(d3));
|
||||
}
|
||||
|
||||
TEST(SerializeExtensionCompactValueAsObjectDeserializeOverflow, TestEnums) {
|
||||
SerializationContext ctx;
|
||||
auto data = getValue<uint32_t >(true, 17);
|
||||
uint16_t res{};
|
||||
auto& ser = ctx.createSerializer();
|
||||
ser.ext(data, CompactValueAsObject{});
|
||||
auto& des = ctx.createDeserializer();
|
||||
des.ext(res, CompactValueAsObject{});
|
||||
auto& rd = bitsery::AdapterAccess::getReader(des);
|
||||
EXPECT_THAT(data, ::testing::Ne(res));
|
||||
EXPECT_THAT(rd.error(), Eq(bitsery::ReaderError::DataOverflow));
|
||||
}
|
||||
|
||||
|
||||
@@ -21,11 +21,12 @@
|
||||
//SOFTWARE.
|
||||
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include "serialization_test_utils.h"
|
||||
#include <bitsery/ext/entropy.h>
|
||||
#include <bitsery/traits/list.h>
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include "serialization_test_utils.h"
|
||||
|
||||
using namespace testing;
|
||||
|
||||
using bitsery::ext::Entropy;
|
||||
|
||||
@@ -20,9 +20,9 @@
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
#include <bitsery/ext/growable.h>
|
||||
#include <gmock/gmock.h>
|
||||
#include "serialization_test_utils.h"
|
||||
#include <bitsery/ext/growable.h>
|
||||
|
||||
using namespace testing;
|
||||
|
||||
@@ -52,9 +52,9 @@ TEST(SerializeExtensionGrowable, WriteSessionsDataAtBufferEndAfterFlush) {
|
||||
ser.value1b(v);
|
||||
});
|
||||
|
||||
EXPECT_THAT(ctx.getBufferSize(), Eq(1));
|
||||
EXPECT_THAT(ctx.getBufferSize(), Eq(1u));
|
||||
ctx.bw->flush();
|
||||
EXPECT_THAT(ctx.getBufferSize(), Gt(1));
|
||||
EXPECT_THAT(ctx.getBufferSize(), Gt(1u));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -20,9 +20,9 @@
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
#include <bitsery/ext/inheritance.h>
|
||||
#include <gmock/gmock.h>
|
||||
#include "serialization_test_utils.h"
|
||||
#include <bitsery/ext/inheritance.h>
|
||||
|
||||
using bitsery::ext::BaseClass;
|
||||
using bitsery::ext::VirtualBaseClass;
|
||||
|
||||
@@ -20,9 +20,9 @@
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
#include <bitsery/ext/pointer.h>
|
||||
#include <gmock/gmock.h>
|
||||
#include "serialization_test_utils.h"
|
||||
#include <bitsery/ext/pointer.h>
|
||||
|
||||
using bitsery::ext::PointerOwner;
|
||||
using bitsery::ext::PointerObserver;
|
||||
@@ -34,34 +34,33 @@ using testing::Eq;
|
||||
|
||||
using SerContext = BasicSerializationContext<bitsery::DefaultConfig, PointerLinkingContext>;
|
||||
|
||||
class SerializeExtensionPointerSerialization: public testing::Test {
|
||||
class SerializeExtensionPointerSerialization : public testing::Test {
|
||||
public:
|
||||
//data used for serialization
|
||||
int16_t d1{1597};
|
||||
int16_t* pd1 = &d1;
|
||||
int16_t *pd1 = &d1;
|
||||
MyEnumClass d2{MyEnumClass::E2};
|
||||
MyEnumClass* pd2 = &d2;
|
||||
MyEnumClass *pd2 = &d2;
|
||||
MyStruct1 d3{184, 897};
|
||||
MyStruct1* pd3 = &d3;
|
||||
MyStruct1 *pd3 = &d3;
|
||||
|
||||
//data used for deserialization
|
||||
int16_t r1{-84};
|
||||
int16_t* pr1 = &r1;
|
||||
int16_t *pr1 = &r1;
|
||||
MyEnumClass r2{MyEnumClass::E4};
|
||||
MyEnumClass* pr2 = &r2;
|
||||
MyEnumClass *pr2 = &r2;
|
||||
MyStruct1 r3{-4984, -14597};
|
||||
MyStruct1* pr3 = &r3;
|
||||
MyStruct1 *pr3 = &r3;
|
||||
|
||||
//null pointers
|
||||
int16_t* p1null = nullptr;
|
||||
MyEnumClass* p2null = nullptr;
|
||||
MyStruct1* p3null = nullptr;
|
||||
|
||||
int16_t *p1null = nullptr;
|
||||
MyEnumClass *p2null = nullptr;
|
||||
MyStruct1 *p3null = nullptr;
|
||||
|
||||
PointerLinkingContext plctx1{};
|
||||
SerContext sctx1{};
|
||||
|
||||
typename SerContext::TSerializer& createSerializer() {
|
||||
typename SerContext::TSerializer &createSerializer() {
|
||||
return sctx1.createSerializer(&plctx1);
|
||||
}
|
||||
|
||||
@@ -71,7 +70,7 @@ public:
|
||||
};
|
||||
|
||||
TEST(SerializeExtensionPointer, RequiresPointerLinkingContext) {
|
||||
MyStruct1* data = nullptr;
|
||||
MyStruct1 *data = nullptr;
|
||||
//linking context
|
||||
PointerLinkingContext plctx1{};
|
||||
SerContext sctx1;
|
||||
@@ -89,17 +88,34 @@ TEST(SerializeExtensionPointer, RequiresPointerLinkingContext) {
|
||||
TEST(SerializeExtensionPointer, PointerLinkingContextAcceptsMultipleSharedOwnersAndReturnSameId) {
|
||||
MyStruct1 data{};
|
||||
//pretend that this is shared ptr
|
||||
MyStruct1* sharedPtr = &data;
|
||||
MyStruct1 *sharedPtr = &data;
|
||||
//linking context
|
||||
PointerLinkingContext plctx1{};
|
||||
EXPECT_THAT(plctx1.getInfoByPtr(sharedPtr, bitsery::ext::PointerOwnershipType::Shared).id, Eq(1));
|
||||
EXPECT_THAT(plctx1.getInfoByPtr(sharedPtr, bitsery::ext::PointerOwnershipType::Shared).id, Eq(1));
|
||||
EXPECT_THAT(plctx1.getInfoByPtr(sharedPtr, bitsery::ext::PointerOwnershipType::Shared).id, Eq(1));
|
||||
EXPECT_THAT(plctx1.getInfoByPtr(sharedPtr, bitsery::ext::PointerOwnershipType::SharedOwner).id, Eq(1));
|
||||
EXPECT_THAT(plctx1.getInfoByPtr(sharedPtr, bitsery::ext::PointerOwnershipType::SharedObserver).id, Eq(1));
|
||||
EXPECT_THAT(plctx1.getInfoByPtr(sharedPtr, bitsery::ext::PointerOwnershipType::SharedOwner).id, Eq(1));
|
||||
}
|
||||
|
||||
TEST(SerializeExtensionPointer, WhenOnlySharedObserverThenPointerLinkingContextIsInvalid) {
|
||||
MyStruct1 data1{};
|
||||
MyStruct1 data2{};
|
||||
//pretend that this is shared ptr
|
||||
MyStruct1 *sharedPtr1 = &data1;
|
||||
MyStruct1 *sharedPtr2 = &data2;
|
||||
//linking context
|
||||
PointerLinkingContext plctx1{};
|
||||
EXPECT_THAT(plctx1.getInfoByPtr(sharedPtr1, bitsery::ext::PointerOwnershipType::SharedObserver).id, Eq(1));
|
||||
EXPECT_THAT(plctx1.getInfoByPtr(sharedPtr2, bitsery::ext::PointerOwnershipType::SharedObserver).id, Eq(2));
|
||||
EXPECT_THAT(plctx1.getInfoByPtr(sharedPtr1, bitsery::ext::PointerOwnershipType::SharedObserver).id, Eq(1));
|
||||
EXPECT_FALSE(plctx1.isValid());
|
||||
EXPECT_THAT(plctx1.getInfoByPtr(sharedPtr1, bitsery::ext::PointerOwnershipType::SharedOwner).id, Eq(1));
|
||||
EXPECT_THAT(plctx1.getInfoByPtr(sharedPtr2, bitsery::ext::PointerOwnershipType::SharedOwner).id, Eq(2));
|
||||
EXPECT_TRUE(plctx1.isValid());
|
||||
}
|
||||
|
||||
TEST_F(SerializeExtensionPointerSerialization, WhenPointersAreNullThenIsValid) {
|
||||
|
||||
auto& ser = createSerializer();
|
||||
auto &ser = createSerializer();
|
||||
ser.ext2b(p1null, PointerOwner{});
|
||||
ser.ext2b(p1null, PointerObserver{});
|
||||
ser.ext(p3null, PointerOwner{});
|
||||
@@ -109,11 +125,12 @@ TEST_F(SerializeExtensionPointerSerialization, WhenPointersAreNullThenIsValid) {
|
||||
|
||||
EXPECT_THAT(plctx1.isValid(), Eq(true));
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
|
||||
TEST(SerializeExtensionPointer, WhenPointerLinkingContextIsNullAndPointerIsNotNullThenAssert) {
|
||||
MyStruct1 tmp;
|
||||
MyStruct1* data = &tmp;
|
||||
MyStruct1 *data = &tmp;
|
||||
//linking context
|
||||
PointerLinkingContext plctx1{};
|
||||
SerContext sctx1;
|
||||
@@ -122,7 +139,7 @@ TEST(SerializeExtensionPointer, WhenPointerLinkingContextIsNullAndPointerIsNotNu
|
||||
|
||||
TEST_F(SerializeExtensionPointerSerialization, WhenPointerOwnerIsNotUniqueThenAssert) {
|
||||
|
||||
auto& ser = createSerializer();
|
||||
auto &ser = createSerializer();
|
||||
ser.ext2b(p1null, PointerOwner{});
|
||||
ser.ext2b(pd1, PointerOwner{});
|
||||
ser.ext4b(pd2, PointerOwner{});
|
||||
@@ -132,7 +149,7 @@ TEST_F(SerializeExtensionPointerSerialization, WhenPointerOwnerIsNotUniqueThenAs
|
||||
}
|
||||
|
||||
TEST_F(SerializeExtensionPointerSerialization, WhenRererencedByPointerIsSameAsPointerOwnerThenAssert1) {
|
||||
auto& ser1 = createSerializer();
|
||||
auto &ser1 = createSerializer();
|
||||
ser1.ext4b(pd2, PointerOwner{});
|
||||
ser1.ext(d3, ReferencedByPointer{});
|
||||
|
||||
@@ -140,14 +157,14 @@ TEST_F(SerializeExtensionPointerSerialization, WhenRererencedByPointerIsSameAsPo
|
||||
}
|
||||
|
||||
TEST_F(SerializeExtensionPointerSerialization, WhenRererencedByPointerIsSameAsPointerOwnerThenAssert2) {
|
||||
auto& ser1 = createSerializer();
|
||||
auto &ser1 = createSerializer();
|
||||
ser1.ext2b(pd1, PointerOwner{});
|
||||
ser1.ext4b(d2, ReferencedByPointer{});
|
||||
EXPECT_DEATH(ser1.ext2b(d1, ReferencedByPointer{}), "");
|
||||
}
|
||||
|
||||
TEST_F(SerializeExtensionPointerSerialization, WhenNonNullPointerIsNullThenAssert) {
|
||||
auto& ser1 = createSerializer();
|
||||
auto &ser1 = createSerializer();
|
||||
EXPECT_DEATH(ser1.ext2b(p1null, PointerOwner{PointerType::NotNull}), "");
|
||||
EXPECT_DEATH(ser1.ext2b(p1null, PointerObserver{PointerType::NotNull}), "");
|
||||
}
|
||||
@@ -155,7 +172,7 @@ TEST_F(SerializeExtensionPointerSerialization, WhenNonNullPointerIsNullThenAsser
|
||||
#endif
|
||||
|
||||
TEST_F(SerializeExtensionPointerSerialization, WhenPointerObserverPointsToOwnerThenIsValid) {
|
||||
auto& ser1 = createSerializer();
|
||||
auto &ser1 = createSerializer();
|
||||
ser1.ext2b(pd1, PointerOwner{});
|
||||
ser1.ext2b(p1null, PointerObserver{});
|
||||
EXPECT_THAT(plctx1.isValid(), Eq(true));
|
||||
@@ -168,7 +185,7 @@ TEST_F(SerializeExtensionPointerSerialization, WhenPointerObserverPointsToOwnerT
|
||||
}
|
||||
|
||||
TEST_F(SerializeExtensionPointerSerialization, ReferenceTypeCanAlsoBeReferencedByPointerObservers) {
|
||||
auto& ser1 = createSerializer();
|
||||
auto &ser1 = createSerializer();
|
||||
ser1.ext2b(p1null, PointerObserver{});
|
||||
EXPECT_THAT(plctx1.isValid(), Eq(true));
|
||||
ser1.ext4b(pd2, PointerObserver{});//points to d2, and d2 is not still marked as owner
|
||||
@@ -180,7 +197,7 @@ TEST_F(SerializeExtensionPointerSerialization, ReferenceTypeCanAlsoBeReferencedB
|
||||
}
|
||||
|
||||
TEST_F(SerializeExtensionPointerSerialization, WhenPointerIsNullThenPointerIdIsZero) {
|
||||
auto& ser1 = createSerializer();
|
||||
auto &ser1 = createSerializer();
|
||||
ser1.ext(p3null, PointerOwner{});
|
||||
ser1.ext2b(p1null, PointerObserver{});
|
||||
sctx1.createDeserializer();
|
||||
@@ -193,7 +210,7 @@ TEST_F(SerializeExtensionPointerSerialization, WhenPointerIsNullThenPointerIdIsZ
|
||||
}
|
||||
|
||||
TEST_F(SerializeExtensionPointerSerialization, PointerIdsStartsFromOne) {
|
||||
auto& ser1 = createSerializer();
|
||||
auto &ser1 = createSerializer();
|
||||
ser1.ext2b(pd1, PointerObserver{});
|
||||
ser1.ext4b(pd2, PointerObserver{});
|
||||
ser1.ext4b(pd2, PointerObserver{});
|
||||
@@ -212,7 +229,7 @@ TEST_F(SerializeExtensionPointerSerialization, PointerIdsStartsFromOne) {
|
||||
}
|
||||
|
||||
TEST_F(SerializeExtensionPointerSerialization, PointerObserversDoesntSerializeObject) {
|
||||
auto& ser1 = createSerializer();
|
||||
auto &ser1 = createSerializer();
|
||||
ser1.ext2b(pd1, PointerObserver{});
|
||||
ser1.ext4b(pd2, PointerObserver{});
|
||||
ser1.ext4b(pd2, PointerObserver{});
|
||||
@@ -221,12 +238,12 @@ TEST_F(SerializeExtensionPointerSerialization, PointerObserversDoesntSerializeOb
|
||||
}
|
||||
|
||||
TEST_F(SerializeExtensionPointerSerialization, ReferencedByPointerSerializesIdAndObject) {
|
||||
auto& ser1 = createSerializer();
|
||||
auto &ser1 = createSerializer();
|
||||
ser1.ext2b(d1, ReferencedByPointer{});
|
||||
ser1.ext4b(d2, ReferencedByPointer{});
|
||||
ser1.ext4b(pd2, PointerObserver{});
|
||||
auto& des = sctx1.createDeserializer();
|
||||
EXPECT_THAT(sctx1.bw->writtenBytesCount(), Eq(3+6));
|
||||
auto &des = sctx1.createDeserializer();
|
||||
EXPECT_THAT(sctx1.bw->writtenBytesCount(), Eq(3 + 6));
|
||||
size_t id{};
|
||||
bitsery::details::readSize(*sctx1.br, id, 10000u);
|
||||
EXPECT_THAT(id, Eq(1));
|
||||
@@ -241,12 +258,12 @@ TEST_F(SerializeExtensionPointerSerialization, ReferencedByPointerSerializesIdAn
|
||||
}
|
||||
|
||||
TEST_F(SerializeExtensionPointerSerialization, PointerOwnerSerializesIdAndObject) {
|
||||
auto& ser1 = createSerializer();
|
||||
auto &ser1 = createSerializer();
|
||||
ser1.ext4b(pd2, PointerOwner{});
|
||||
ser1.ext(pd3, PointerOwner{});
|
||||
auto& des1 = sctx1.createDeserializer();
|
||||
auto &des1 = sctx1.createDeserializer();
|
||||
//2x ids + int32_t + MyStruct1
|
||||
EXPECT_THAT(sctx1.bw->writtenBytesCount(), Eq(2 + 4 + MyStruct1::SIZE ));
|
||||
EXPECT_THAT(sctx1.bw->writtenBytesCount(), Eq(2 + 4 + MyStruct1::SIZE));
|
||||
size_t id;
|
||||
bitsery::details::readSize(*sctx1.br, id, 10000u);
|
||||
des1.value4b(r2);
|
||||
@@ -256,25 +273,25 @@ TEST_F(SerializeExtensionPointerSerialization, PointerOwnerSerializesIdAndObject
|
||||
EXPECT_THAT(r3, Eq(*pd3));
|
||||
}
|
||||
|
||||
class SerializeExtensionPointerDeserialization: public SerializeExtensionPointerSerialization {
|
||||
class SerializeExtensionPointerDeserialization : public SerializeExtensionPointerSerialization {
|
||||
public:
|
||||
|
||||
typename SerContext::TSerializer& createSerializer() {
|
||||
typename SerContext::TSerializer &createSerializer() {
|
||||
return sctx1.createSerializer(&plctx1);
|
||||
}
|
||||
|
||||
typename SerContext::TDeserializer& createDeserializer() {
|
||||
typename SerContext::TDeserializer &createDeserializer() {
|
||||
return sctx1.createDeserializer(&plctx1);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
TEST_F(SerializeExtensionPointerDeserialization, ReferencedByPointer) {
|
||||
auto& ser = createSerializer();
|
||||
auto &ser = createSerializer();
|
||||
ser.ext2b(d1, ReferencedByPointer{});
|
||||
ser.ext4b(d2, ReferencedByPointer{});
|
||||
ser.ext(d3, ReferencedByPointer{});
|
||||
auto& des = createDeserializer();
|
||||
auto &des = createDeserializer();
|
||||
des.ext2b(r1, ReferencedByPointer{});
|
||||
des.ext4b(r2, ReferencedByPointer{});
|
||||
des.ext(r3, ReferencedByPointer{});
|
||||
@@ -285,10 +302,10 @@ TEST_F(SerializeExtensionPointerDeserialization, ReferencedByPointer) {
|
||||
}
|
||||
|
||||
TEST_F(SerializeExtensionPointerDeserialization, WhenReferencedByPointerReadsNullPointerThenInvalidPointerError) {
|
||||
auto& ser = createSerializer();
|
||||
auto &ser = createSerializer();
|
||||
bitsery::details::writeSize(*sctx1.bw, 0u);
|
||||
ser.ext2b(d1, ReferencedByPointer{});
|
||||
auto& des = createDeserializer();
|
||||
auto &des = createDeserializer();
|
||||
des.ext2b(r1, ReferencedByPointer{});
|
||||
EXPECT_THAT(sctx1.br->error(), Eq(bitsery::ReaderError::InvalidPointer));
|
||||
}
|
||||
@@ -296,21 +313,21 @@ TEST_F(SerializeExtensionPointerDeserialization, WhenReferencedByPointerReadsNul
|
||||
TEST_F(SerializeExtensionPointerDeserialization, WhenNonNullPointerIsNullThenInvalidPointerError) {
|
||||
createSerializer();
|
||||
bitsery::details::writeSize(*sctx1.bw, 0u);
|
||||
auto& des1 = createDeserializer();
|
||||
auto &des1 = createDeserializer();
|
||||
des1.ext2b(p1null, PointerOwner{PointerType::NotNull});
|
||||
EXPECT_THAT(sctx1.br->error(), Eq(bitsery::ReaderError::InvalidPointer));
|
||||
|
||||
auto& des2 = createDeserializer();
|
||||
auto &des2 = createDeserializer();
|
||||
des2.ext2b(p1null, PointerObserver{PointerType::NotNull});
|
||||
EXPECT_THAT(sctx1.br->error(), Eq(bitsery::ReaderError::InvalidPointer));
|
||||
}
|
||||
|
||||
TEST_F(SerializeExtensionPointerDeserialization, PointerOwnerCreatesObjects) {
|
||||
auto& ser = createSerializer();
|
||||
auto &ser = createSerializer();
|
||||
ser.ext2b(pd1, PointerOwner{});
|
||||
ser.ext4b(pd2, PointerOwner{});
|
||||
ser.ext(pd3, PointerOwner{});
|
||||
auto& des = createDeserializer();
|
||||
auto &des = createDeserializer();
|
||||
des.ext2b(p1null, PointerOwner{});
|
||||
des.ext4b(p2null, PointerOwner{});
|
||||
des.ext(p3null, PointerOwner{});
|
||||
@@ -325,15 +342,15 @@ TEST_F(SerializeExtensionPointerDeserialization, PointerOwnerCreatesObjects) {
|
||||
}
|
||||
|
||||
TEST_F(SerializeExtensionPointerDeserialization, PointerOwnerDestroysObjects) {
|
||||
auto& ser = createSerializer();
|
||||
auto &ser = createSerializer();
|
||||
ser.ext2b(p1null, PointerOwner{});
|
||||
ser.ext4b(p2null, PointerOwner{});
|
||||
ser.ext(p3null, PointerOwner{});
|
||||
auto& des = createDeserializer();
|
||||
auto &des = createDeserializer();
|
||||
//pr cannot link to local variables, need to allocate them separately
|
||||
pr1 = new int16_t{};
|
||||
pr2 = new MyEnumClass{};
|
||||
pr3 = new MyStruct1{3,4};
|
||||
pr3 = new MyStruct1{3, 4};
|
||||
des.ext2b(pr1, PointerOwner{});
|
||||
des.ext4b(pr2, PointerOwner{});
|
||||
des.ext(pr3, PointerOwner{});
|
||||
@@ -345,7 +362,7 @@ TEST_F(SerializeExtensionPointerDeserialization, PointerOwnerDestroysObjects) {
|
||||
}
|
||||
|
||||
TEST_F(SerializeExtensionPointerDeserialization, PointerObserver) {
|
||||
auto& ser = createSerializer();
|
||||
auto &ser = createSerializer();
|
||||
//first owner, than observer
|
||||
ser.ext4b(d2, ReferencedByPointer{});
|
||||
ser.ext2b(p1null, PointerObserver{});
|
||||
@@ -353,7 +370,7 @@ TEST_F(SerializeExtensionPointerDeserialization, PointerObserver) {
|
||||
//first observer, than owner
|
||||
ser.ext(pd3, PointerObserver{});
|
||||
ser.ext(pd3, PointerOwner{});
|
||||
auto& des = createDeserializer();
|
||||
auto &des = createDeserializer();
|
||||
des.ext4b(r2, ReferencedByPointer{});
|
||||
des.ext2b(pr1, PointerObserver{});
|
||||
des.ext4b(p2null, PointerObserver{});
|
||||
@@ -371,18 +388,18 @@ TEST_F(SerializeExtensionPointerDeserialization, PointerObserver) {
|
||||
EXPECT_THAT(pr3, Eq(&r3));
|
||||
}
|
||||
|
||||
|
||||
struct Test1Data {
|
||||
std::vector<MyStruct1> vdata;
|
||||
std::vector<MyStruct1*> vptr;
|
||||
std::vector<MyStruct1 *> vptr;
|
||||
MyStruct1 o1;
|
||||
MyStruct1* po1;
|
||||
MyStruct1 *po1;
|
||||
int32_t i1;
|
||||
int32_t* pi1;
|
||||
template <typename S>
|
||||
void serialize(S& s) {
|
||||
int32_t *pi1;
|
||||
|
||||
template<typename S>
|
||||
void serialize(S &s) {
|
||||
//set container elements to be candidates for non-owning pointers
|
||||
s.container(vdata, 100, [&s](MyStruct1& d){
|
||||
s.container(vdata, 100, [&s](MyStruct1 &d) {
|
||||
s.ext(d, ReferencedByPointer{});
|
||||
});
|
||||
//contains non owning pointers
|
||||
@@ -390,7 +407,7 @@ struct Test1Data {
|
||||
//IMPORTANT !!!
|
||||
// ALWAYS ACCEPT BY REFERENCE like this: T* (&obj)
|
||||
//
|
||||
s.container(vptr, 100, [&s](MyStruct1* (&d)){
|
||||
s.container(vptr, 100, [&s](MyStruct1 *(&d)) {
|
||||
s.ext(d, PointerObserver{});
|
||||
});
|
||||
//just a regular fields
|
||||
@@ -407,15 +424,15 @@ struct Test1Data {
|
||||
TEST(SerializeExtensionPointer, IntegrationTest) {
|
||||
|
||||
Test1Data data{};
|
||||
data.vdata.push_back({165,-45});
|
||||
data.vdata.push_back({7895,-1576});
|
||||
data.vdata.push_back({5987,-798});
|
||||
data.vdata.push_back({165, -45});
|
||||
data.vdata.push_back({7895, -1576});
|
||||
data.vdata.push_back({5987, -798});
|
||||
//container of non owning pointers (observers)
|
||||
data.vptr.push_back(nullptr);
|
||||
data.vptr.push_back(std::addressof(data.vdata[0]));
|
||||
data.vptr.push_back(std::addressof(data.vdata[2]));
|
||||
//regular fields
|
||||
data.o1 = MyStruct1{145,948};
|
||||
data.o1 = MyStruct1{145, 948};
|
||||
data.i1 = 945415;
|
||||
//observer
|
||||
data.po1 = std::addressof(data.vdata[1]);
|
||||
@@ -447,4 +464,80 @@ TEST(SerializeExtensionPointer, IntegrationTest) {
|
||||
//free owning raw pointers
|
||||
delete data.pi1;
|
||||
delete res.pi1;
|
||||
}
|
||||
}
|
||||
|
||||
TEST(SerializeExtensionPointer, PointerOwnerWithNonPolymorphicTypeCanUseLambdaOverload) {
|
||||
const int32_t NEW_VALUE = 2;
|
||||
const int32_t OLD_VALUE = 1;
|
||||
MyStruct1 *data = new MyStruct1{NEW_VALUE, NEW_VALUE};
|
||||
MyStruct1 *res = new MyStruct1{OLD_VALUE, OLD_VALUE};
|
||||
//linking context
|
||||
PointerLinkingContext plctx1{};
|
||||
SerContext sctx1;
|
||||
auto &ser = sctx1.createSerializer(&plctx1);
|
||||
ser.ext(data, PointerOwner{}, [&ser](MyStruct1 &o) {
|
||||
//serialize only one field
|
||||
ser.value4b(o.i1);
|
||||
});
|
||||
auto &des = sctx1.createDeserializer(&plctx1);
|
||||
des.ext(res, PointerOwner{}, [&des](MyStruct1 &o) {
|
||||
//deserialize only one field
|
||||
des.value4b(o.i1);
|
||||
});
|
||||
|
||||
EXPECT_THAT(res->i1, Eq(NEW_VALUE));
|
||||
EXPECT_THAT(res->i2, Eq(OLD_VALUE));//we didn't serialized that
|
||||
|
||||
delete data;
|
||||
delete res;
|
||||
}
|
||||
|
||||
TEST(SerializeExtensionPointer, ReferencedByPointerCanUseLambdaOverload) {
|
||||
const int32_t NEW_VALUE = 2;
|
||||
const int32_t OLD_VALUE = 1;
|
||||
MyStruct1 data = MyStruct1{NEW_VALUE, NEW_VALUE};
|
||||
MyStruct1 res = MyStruct1{OLD_VALUE, OLD_VALUE};
|
||||
//linking context
|
||||
PointerLinkingContext plctx1{};
|
||||
SerContext sctx1;
|
||||
auto &ser = sctx1.createSerializer(&plctx1);
|
||||
ser.ext(data, ReferencedByPointer{}, [&ser](MyStruct1 &o) {
|
||||
//serialize only one field
|
||||
ser.value4b(o.i1);
|
||||
});
|
||||
auto &des = sctx1.createDeserializer(&plctx1);
|
||||
des.ext(res, ReferencedByPointer{}, [&des](MyStruct1 &o) {
|
||||
//deserialize only one field
|
||||
des.value4b(o.i1);
|
||||
});
|
||||
|
||||
EXPECT_THAT(res.i1, Eq(NEW_VALUE));
|
||||
EXPECT_THAT(res.i2, Eq(OLD_VALUE));//we didn't serialized that
|
||||
}
|
||||
|
||||
TEST(SerializeExtensionPointer, PointerOwnerCanUseValueOverload) {
|
||||
auto *data = new int64_t{49845894};
|
||||
auto *res = new int64_t{-78548415};
|
||||
|
||||
PointerLinkingContext plctx1{};
|
||||
SerContext sctx1;
|
||||
sctx1.createSerializer(&plctx1).ext8b(data, PointerOwner{});
|
||||
sctx1.createDeserializer(&plctx1).ext8b(res, PointerOwner{});
|
||||
|
||||
EXPECT_THAT(*res, Eq(*data));
|
||||
|
||||
delete data;
|
||||
delete res;
|
||||
}
|
||||
|
||||
TEST(SerializeExtensionPointer, ReferencedByPointerCanUseValueOverload) {
|
||||
int64_t data{49845894};
|
||||
int64_t res{-78548415};
|
||||
|
||||
PointerLinkingContext plctx1{};
|
||||
SerContext sctx1;
|
||||
sctx1.createSerializer(&plctx1).ext8b(data, ReferencedByPointer{});
|
||||
sctx1.createDeserializer(&plctx1).ext8b(res, ReferencedByPointer{});
|
||||
|
||||
EXPECT_THAT(res, Eq(data));
|
||||
}
|
||||
|
||||
@@ -20,59 +20,71 @@
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
|
||||
#include <bitsery/ext/inheritance.h>
|
||||
#include <bitsery/ext/pointer.h>
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include "serialization_test_utils.h"
|
||||
#include <bitsery/ext/pointer.h>
|
||||
|
||||
using bitsery::ext::BaseClass;
|
||||
using bitsery::ext::VirtualBaseClass;
|
||||
|
||||
using bitsery::ext::InheritanceContext;
|
||||
using bitsery::ext::PointerLinkingContext;
|
||||
using bitsery::ext::PolymorphicContext;
|
||||
using bitsery::ext::StandardRTTI;
|
||||
|
||||
using bitsery::ext::PointerOwner;
|
||||
using bitsery::ext::PointerObserver;
|
||||
using bitsery::ext::ReferencedByPointer;
|
||||
using bitsery::ext::PointerLinkingContext;
|
||||
using bitsery::ext::PointerType;
|
||||
|
||||
using testing::Eq;
|
||||
|
||||
using TContext = std::tuple<PointerLinkingContext, bitsery::ext::InheritanceContext>;
|
||||
using TContext = std::tuple<PointerLinkingContext, InheritanceContext, PolymorphicContext<StandardRTTI>>;
|
||||
using SerContext = BasicSerializationContext<bitsery::DefaultConfig, TContext>;
|
||||
|
||||
//this is useful for PolymorphicContext to bind classes to serializer/deserializer
|
||||
using TSerializer = typename SerContext::TSerializer;
|
||||
using TDeserializer = typename SerContext::TDeserializer;
|
||||
|
||||
/*
|
||||
* base class
|
||||
*/
|
||||
struct Base {
|
||||
uint8_t x{};
|
||||
|
||||
virtual ~Base() = default;
|
||||
};
|
||||
|
||||
template <typename S>
|
||||
void serialize(S& s, Base& o) {
|
||||
template<typename S>
|
||||
void serialize(S &s, Base &o) {
|
||||
s.value1b(o.x);
|
||||
}
|
||||
|
||||
struct Derived1:virtual Base {
|
||||
struct Derived1 : virtual Base {
|
||||
uint8_t y1{};
|
||||
};
|
||||
|
||||
template <typename S>
|
||||
void serialize(S& s, Derived1& o) {
|
||||
template<typename S>
|
||||
void serialize(S &s, Derived1 &o) {
|
||||
s.ext(o, VirtualBaseClass<Base>{});
|
||||
s.value1b(o.y1);
|
||||
}
|
||||
|
||||
struct Derived2:virtual Base {
|
||||
struct Derived2 : virtual Base {
|
||||
uint8_t y2{};
|
||||
};
|
||||
|
||||
template <typename S>
|
||||
void serialize(S& s, Derived2& o) {
|
||||
template<typename S>
|
||||
void serialize(S &s, Derived2 &o) {
|
||||
s.ext(o, VirtualBaseClass<Base>{});
|
||||
s.value1b(o.y2);
|
||||
}
|
||||
|
||||
struct MultipleVirtualInheritance: Derived1, Derived2 {
|
||||
struct MultipleVirtualInheritance : Derived1, Derived2 {
|
||||
int8_t z{};
|
||||
|
||||
MultipleVirtualInheritance() = default;
|
||||
|
||||
MultipleVirtualInheritance(uint8_t x_, uint8_t y1_, uint8_t y2_, uint8_t z_) {
|
||||
@@ -82,8 +94,8 @@ struct MultipleVirtualInheritance: Derived1, Derived2 {
|
||||
z = z_;
|
||||
}
|
||||
|
||||
template <typename S>
|
||||
void serialize(S& s) {
|
||||
template<typename S>
|
||||
void serialize(S &s) {
|
||||
s.ext(*this, BaseClass<Derived1>{});
|
||||
s.ext(*this, BaseClass<Derived2>{});
|
||||
s.value1b(z);
|
||||
@@ -91,40 +103,69 @@ struct MultipleVirtualInheritance: Derived1, Derived2 {
|
||||
|
||||
};
|
||||
|
||||
//define PolymorphicBase relationships for runtime polymorphism
|
||||
//this class has no relationships specified via PolymorphicBaseClass
|
||||
struct NoRelationshipSpecifiedDerived : Base {
|
||||
};
|
||||
|
||||
//these classes will be used to "cheat" a little bit when testing deserialization flows
|
||||
struct BaseClone {
|
||||
uint8_t x{};
|
||||
|
||||
virtual ~BaseClone() = default;
|
||||
};
|
||||
|
||||
template<typename S>
|
||||
void serialize(S &s, BaseClone &o) {
|
||||
s.value1b(o.x);
|
||||
}
|
||||
|
||||
//define relationships between base class and derived classes for runtime polymorphism
|
||||
|
||||
namespace bitsery {
|
||||
namespace ext {
|
||||
|
||||
template <>
|
||||
struct PolymorphicBaseClass<Base>: DerivedClasses<Derived1, Derived2> {};
|
||||
template<>
|
||||
struct PolymorphicBaseClass<Base> : PolymorphicDerivedClasses<Derived1, Derived2> {
|
||||
};
|
||||
|
||||
template <>
|
||||
struct PolymorphicBaseClass<Derived1>: DerivedClasses<MultipleVirtualInheritance> {};
|
||||
// this is commented on purpose, to test scenario when base class is registered (Base)
|
||||
// but using instance of Derived1 which is not registered as base
|
||||
// template<>
|
||||
// struct PolymorphicBaseClass<Derived1> : PolymorphicDerivedClasses<MultipleVirtualInheritance> {
|
||||
// };
|
||||
|
||||
template <>
|
||||
struct PolymorphicBaseClass<Derived2>: DerivedClasses<MultipleVirtualInheritance> {};
|
||||
template<>
|
||||
struct PolymorphicBaseClass<Derived2> : PolymorphicDerivedClasses<MultipleVirtualInheritance> {
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class SerializeExtensionPointerPolymorphicTypes: public testing::Test {
|
||||
class SerializeExtensionPointerPolymorphicTypes : public testing::Test {
|
||||
public:
|
||||
|
||||
TContext plctx1{};
|
||||
SerContext sctx1{};
|
||||
TContext plctx{};
|
||||
SerContext sctx{};
|
||||
|
||||
typename SerContext::TSerializer& createSerializer() {
|
||||
return sctx1.createSerializer(&plctx1);
|
||||
typename SerContext::TSerializer &createSerializer() {
|
||||
auto &res = sctx.createSerializer(&plctx);
|
||||
std::get<2>(plctx).clear();
|
||||
//bind serializer with classes
|
||||
std::get<2>(plctx).registerBasesList<SerContext::TSerializer>(bitsery::ext::PolymorphicClassesList<Base>{});
|
||||
return res;
|
||||
}
|
||||
|
||||
typename SerContext::TDeserializer& createDeserializer() {
|
||||
return sctx1.createDeserializer(&plctx1);
|
||||
typename SerContext::TDeserializer &createDeserializer() {
|
||||
auto &res = sctx.createDeserializer(&plctx);
|
||||
std::get<2>(plctx).clear();
|
||||
//bind deserializer with classes
|
||||
std::get<2>(plctx).registerBasesList<SerContext::TDeserializer>(bitsery::ext::PolymorphicClassesList<Base>{});
|
||||
return res;
|
||||
}
|
||||
|
||||
bool isPointerContextValid() {
|
||||
return std::get<0>(plctx1).isValid();
|
||||
return std::get<0>(plctx).isValid();
|
||||
}
|
||||
|
||||
virtual void TearDown() override {
|
||||
@@ -133,9 +174,9 @@ public:
|
||||
};
|
||||
|
||||
TEST_F(SerializeExtensionPointerPolymorphicTypes, Data0Result0) {
|
||||
Base* baseData = nullptr;
|
||||
Base *baseData = nullptr;
|
||||
createSerializer().ext(baseData, PointerOwner{});
|
||||
Base* baseRes = nullptr;
|
||||
Base *baseRes = nullptr;
|
||||
createDeserializer().ext(baseRes, PointerOwner{});
|
||||
|
||||
EXPECT_THAT(baseRes, ::testing::IsNull());
|
||||
@@ -143,10 +184,10 @@ TEST_F(SerializeExtensionPointerPolymorphicTypes, Data0Result0) {
|
||||
}
|
||||
|
||||
TEST_F(SerializeExtensionPointerPolymorphicTypes, Data0Result1) {
|
||||
Base* baseData = nullptr;
|
||||
Base *baseData = nullptr;
|
||||
createSerializer().ext(baseData, PointerOwner{});
|
||||
|
||||
Base* baseRes = new Derived1{};
|
||||
Base *baseRes = new Derived1{};
|
||||
createDeserializer().ext(baseRes, PointerOwner{});
|
||||
|
||||
EXPECT_THAT(baseRes, ::testing::IsNull());
|
||||
@@ -157,13 +198,13 @@ TEST_F(SerializeExtensionPointerPolymorphicTypes, Data1Result0) {
|
||||
Derived1 d1{};
|
||||
d1.x = 3;
|
||||
d1.y1 = 78;
|
||||
Base* baseData = &d1;
|
||||
Base *baseData = &d1;
|
||||
createSerializer().ext(baseData, PointerOwner{});
|
||||
Base* baseRes = nullptr;
|
||||
Base *baseRes = nullptr;
|
||||
createDeserializer().ext(baseRes, PointerOwner{});
|
||||
|
||||
auto* data = dynamic_cast<Derived1*>(baseData);
|
||||
auto* res = dynamic_cast<Derived1*>(baseRes);
|
||||
auto *data = dynamic_cast<Derived1 *>(baseData);
|
||||
auto *res = dynamic_cast<Derived1 *>(baseRes);
|
||||
|
||||
EXPECT_THAT(baseRes, ::testing::NotNull());
|
||||
EXPECT_THAT(data, ::testing::NotNull());
|
||||
@@ -177,13 +218,13 @@ TEST_F(SerializeExtensionPointerPolymorphicTypes, Data1Result1) {
|
||||
Derived1 d1{};
|
||||
d1.x = 3;
|
||||
d1.y1 = 78;
|
||||
Base* baseData = &d1;
|
||||
Base *baseData = &d1;
|
||||
createSerializer().ext(baseData, PointerOwner{});
|
||||
Base* baseRes = &d1;
|
||||
Base *baseRes = &d1;
|
||||
createDeserializer().ext(baseRes, PointerOwner{});
|
||||
|
||||
auto* data = dynamic_cast<Derived1*>(baseData);
|
||||
auto* res = dynamic_cast<Derived1*>(baseRes);
|
||||
auto *data = dynamic_cast<Derived1 *>(baseData);
|
||||
auto *res = dynamic_cast<Derived1 *>(baseRes);
|
||||
|
||||
EXPECT_THAT(baseRes, ::testing::NotNull());
|
||||
EXPECT_THAT(data, ::testing::NotNull());
|
||||
@@ -192,19 +233,19 @@ TEST_F(SerializeExtensionPointerPolymorphicTypes, Data1Result1) {
|
||||
EXPECT_THAT(res->y1, Eq(data->y1));
|
||||
}
|
||||
|
||||
TEST_F(SerializeExtensionPointerPolymorphicTypes, ComplexTypeData1Result0) {
|
||||
TEST_F(SerializeExtensionPointerPolymorphicTypes, ComplexTypeWithVirtualInheritanceData1Result0) {
|
||||
MultipleVirtualInheritance md1{};
|
||||
md1.x = 3;
|
||||
md1.y1 = 78;
|
||||
md1.y2 = 14;
|
||||
md1.z = -33;
|
||||
Base* baseData = &md1;
|
||||
Base *baseData = &md1;
|
||||
createSerializer().ext(baseData, PointerOwner{});
|
||||
Base* baseRes = nullptr;
|
||||
Base *baseRes = nullptr;
|
||||
createDeserializer().ext(baseRes, PointerOwner{});
|
||||
|
||||
auto* data = dynamic_cast<MultipleVirtualInheritance*>(baseData);
|
||||
auto* res = dynamic_cast<MultipleVirtualInheritance*>(baseRes);
|
||||
auto *data = dynamic_cast<MultipleVirtualInheritance *>(baseData);
|
||||
auto *res = dynamic_cast<MultipleVirtualInheritance *>(baseRes);
|
||||
|
||||
EXPECT_THAT(baseRes, ::testing::NotNull());
|
||||
EXPECT_THAT(data, ::testing::NotNull());
|
||||
@@ -222,28 +263,66 @@ TEST_F(SerializeExtensionPointerPolymorphicTypes, WhenResultIsDifferentTypeThenR
|
||||
md1.y1 = 78;
|
||||
md1.y2 = 14;
|
||||
md1.z = -33;
|
||||
Base* baseData = &md1;
|
||||
Base *baseData = &md1;
|
||||
createSerializer().ext(baseData, PointerOwner{});
|
||||
Base* baseRes = new Derived1{};
|
||||
EXPECT_THAT(dynamic_cast<MultipleVirtualInheritance*>(baseRes), ::testing::IsNull());
|
||||
Base *baseRes = new Derived1{};
|
||||
EXPECT_THAT(dynamic_cast<MultipleVirtualInheritance *>(baseRes), ::testing::IsNull());
|
||||
createDeserializer().ext(baseRes, PointerOwner{});
|
||||
EXPECT_THAT(dynamic_cast<MultipleVirtualInheritance*>(baseRes), ::testing::NotNull());
|
||||
EXPECT_THAT(dynamic_cast<MultipleVirtualInheritance *>(baseRes), ::testing::NotNull());
|
||||
delete baseRes;
|
||||
}
|
||||
|
||||
//struct UnknownType:Base {
|
||||
//};
|
||||
//
|
||||
//template <typename S>
|
||||
//void serialize(S& s, UnknownType& o) {
|
||||
// s.ext(o, VirtualBaseClass<Base>{});
|
||||
//}
|
||||
#ifndef NDEBUG
|
||||
|
||||
// todo reimplement whole polymorphism thing, because with current solution this test fails
|
||||
//TEST(SerializeExtensionPointerPolymorphicTypesErrors, WhenSerializingUnknownTypeThenAssert) {
|
||||
// TContext plctx1{};
|
||||
// SerContext sctx1{};
|
||||
// UnknownType obj{};
|
||||
// UnknownType* unknownPtr = &obj;
|
||||
// EXPECT_DEATH(sctx1.createSerializer(&plctx1).ext(unknownPtr, PointerOwner{}), "");
|
||||
//}
|
||||
TEST_F(SerializeExtensionPointerPolymorphicTypes,
|
||||
WhenSerializingDerivedTypeWithoutSpecifiedRelationshipsWithBaseThenAssert) {
|
||||
|
||||
NoRelationshipSpecifiedDerived md1;//this class has no relationships specified via PolymorphicBaseClass
|
||||
Base *baseData = &md1;
|
||||
EXPECT_DEATH(createSerializer().ext(baseData, PointerOwner{}), "");
|
||||
}
|
||||
|
||||
TEST_F(SerializeExtensionPointerPolymorphicTypes,
|
||||
WhenDeserializingDerivedTypeNotRegisteredWithPolymorphicContextThenAssert) {
|
||||
|
||||
Derived1 d1{};
|
||||
Base *baseData = &d1;
|
||||
createSerializer().ext(baseData, PointerOwner{});
|
||||
|
||||
BaseClone *baseRes = nullptr; //this class is not registered
|
||||
EXPECT_DEATH(createDeserializer().ext(baseRes, PointerOwner{}), "");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
TEST_F(SerializeExtensionPointerPolymorphicTypes,
|
||||
CompileTimeTypeIsDerivedAndReachableFromBaseRegisteredWithPolymorphicContext) {
|
||||
|
||||
MultipleVirtualInheritance md;
|
||||
Derived2 *derivedData = &md;//this class is not registered via PolymorphicContext
|
||||
|
||||
createSerializer().ext(derivedData, PointerOwner{});
|
||||
Derived2 *derivedRes = new Derived2{};
|
||||
EXPECT_THAT(dynamic_cast<MultipleVirtualInheritance *>(derivedRes), ::testing::IsNull());
|
||||
createDeserializer().ext(derivedRes, PointerOwner{});
|
||||
EXPECT_THAT(dynamic_cast<MultipleVirtualInheritance *>(derivedRes), ::testing::NotNull());
|
||||
delete derivedRes;
|
||||
|
||||
}
|
||||
|
||||
|
||||
TEST_F(SerializeExtensionPointerPolymorphicTypes,
|
||||
WhenPolymorphicTypeNotFoundDuringDeserializionThenInvalidPointerError) {
|
||||
|
||||
Derived1 d1{};
|
||||
Base *baseData = &d1;
|
||||
createSerializer().ext(baseData, PointerOwner{});
|
||||
|
||||
BaseClone *baseRes = nullptr; //this class will be registered, but it doesn't have relationships specified via PolymorphicBaseClass
|
||||
auto &des = sctx.createDeserializer(&plctx);
|
||||
auto &pc = std::get<2>(plctx);
|
||||
pc.clear();
|
||||
pc.registerBasesList<SerContext::TDeserializer>(bitsery::ext::PolymorphicClassesList<BaseClone>{});
|
||||
des.ext(baseRes, PointerOwner{});
|
||||
EXPECT_THAT(sctx.br->error(), Eq(bitsery::ReaderError::InvalidPointer));
|
||||
}
|
||||
|
||||
@@ -20,14 +20,14 @@
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include "serialization_test_utils.h"
|
||||
|
||||
#include <bitsery/ext/std_map.h>
|
||||
#include <bitsery/ext/entropy.h>
|
||||
#include <unordered_map>
|
||||
#include <bitsery/traits/string.h>
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include "serialization_test_utils.h"
|
||||
|
||||
using StdMap = bitsery::ext::StdMap;
|
||||
|
||||
using testing::Eq;
|
||||
|
||||
@@ -33,10 +33,10 @@
|
||||
|
||||
using StdOptional = bitsery::ext::StdOptional;
|
||||
|
||||
using testing::Eq;
|
||||
using BPSer = SerializationContext::TSerializer::BPEnabledType;
|
||||
using BPDes = SerializationContext::TDeserializer::BPEnabledType;
|
||||
|
||||
using BPSer = bitsery::BasicSerializer<Writer, true>;
|
||||
using BPDes = bitsery::BasicDeserializer<Reader, true>;
|
||||
using testing::Eq;
|
||||
|
||||
template <typename T>
|
||||
void test(SerializationContext& ctx, const T& v, T& r) {
|
||||
@@ -117,7 +117,6 @@ TEST(SerializeExtensionStdOptional, NoAlignAfterStateWriteRead) {
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
EXPECT_THAT(range.getRequiredBits() + 1, ::testing::Lt(8));
|
||||
EXPECT_THAT(ctx.getBufferSize(), Eq(1));
|
||||
EXPECT_THAT(t1.value(), Eq(r1.value()));
|
||||
|
||||
@@ -20,9 +20,10 @@
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
#include <bitsery/ext/std_queue.h>
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include "serialization_test_utils.h"
|
||||
#include <bitsery/ext/std_queue.h>
|
||||
|
||||
using StdQueue = bitsery::ext::StdQueue;
|
||||
|
||||
|
||||
@@ -20,12 +20,12 @@
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include "serialization_test_utils.h"
|
||||
|
||||
#include <bitsery/ext/std_set.h>
|
||||
#include <set>
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include "serialization_test_utils.h"
|
||||
|
||||
using StdSet = bitsery::ext::StdSet;
|
||||
|
||||
using testing::Eq;
|
||||
@@ -35,7 +35,7 @@ class SerializeExtensionStdSet : public testing::Test {
|
||||
public:
|
||||
using TContainer = T;
|
||||
const TContainer src = {4, 8, 48, 4, 9845, 64, 8};
|
||||
TContainer res{};
|
||||
TContainer res{78,74,154,8};
|
||||
};
|
||||
|
||||
using SerializeExtensionStdSetTypes = ::testing::Types<
|
||||
@@ -65,7 +65,7 @@ TEST(SerializeExtensionStdSet, ObjectSyntax) {
|
||||
TEST(SerializeExtensionStdSet, FunctionSyntax) {
|
||||
SerializationContext ctx1;
|
||||
std::unordered_multiset<int32_t> t1{54,-484,841,79};
|
||||
std::unordered_multiset<int32_t> r1{};
|
||||
std::unordered_multiset<int32_t> r1{74,878,15,16,-7,5,-4,8,7};
|
||||
auto& ser = ctx1.createSerializer();
|
||||
ser.ext(t1, StdSet{10}, [&ser](int32_t& v) {
|
||||
ser.value4b(v);
|
||||
@@ -75,4 +75,4 @@ TEST(SerializeExtensionStdSet, FunctionSyntax) {
|
||||
des.value4b(v);
|
||||
});
|
||||
EXPECT_THAT(r1, Eq(t1));
|
||||
}
|
||||
}
|
||||
|
||||
690
tests/serialization_ext_std_smart_ptr.cpp
Normal file
690
tests/serialization_ext_std_smart_ptr.cpp
Normal file
@@ -0,0 +1,690 @@
|
||||
//MIT License
|
||||
//
|
||||
//Copyright (c) 2017 Mindaugas Vinkelis
|
||||
//
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
//of this software and associated documentation files (the "Software"), to deal
|
||||
//in the Software without restriction, including without limitation the rights
|
||||
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
//copies of the Software, and to permit persons to whom the Software is
|
||||
//furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//The above copyright notice and this permission notice shall be included in all
|
||||
//copies or substantial portions of the Software.
|
||||
//
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
#include <bitsery/ext/inheritance.h>
|
||||
#include <bitsery/ext/pointer.h>
|
||||
#include <bitsery/ext/std_smart_ptr.h>
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include "serialization_test_utils.h"
|
||||
|
||||
using bitsery::ext::BaseClass;
|
||||
using bitsery::ext::VirtualBaseClass;
|
||||
|
||||
using bitsery::ext::InheritanceContext;
|
||||
using bitsery::ext::PointerLinkingContext;
|
||||
using bitsery::ext::PolymorphicContext;
|
||||
using bitsery::ext::StandardRTTI;
|
||||
using bitsery::ext::PointerType;
|
||||
|
||||
using bitsery::ext::StdSmartPtr;
|
||||
using bitsery::ext::PointerObserver;
|
||||
|
||||
using testing::Eq;
|
||||
using testing::Ne;
|
||||
|
||||
struct Base {
|
||||
uint8_t x{};
|
||||
|
||||
virtual ~Base() = default;
|
||||
};
|
||||
|
||||
template<typename S>
|
||||
void serialize(S &s, Base &o) {
|
||||
s.value1b(o.x);
|
||||
}
|
||||
|
||||
struct Derived : virtual Base {
|
||||
uint8_t y{};
|
||||
|
||||
Derived() = default;
|
||||
|
||||
Derived(uint8_t x_, uint8_t y_) {
|
||||
x = x_;
|
||||
y = y_;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename S>
|
||||
void serialize(S &s, Derived &o) {
|
||||
s.ext(o, VirtualBaseClass<Base>{});
|
||||
s.value1b(o.y);
|
||||
}
|
||||
|
||||
struct MoreDerived : Derived {
|
||||
int8_t z{};
|
||||
|
||||
MoreDerived() = default;
|
||||
|
||||
MoreDerived(uint8_t x_, uint8_t y_, uint8_t z_) : Derived(x_, y_) {
|
||||
z = z_;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template<typename S>
|
||||
void serialize(S &s, MoreDerived &o) {
|
||||
s.ext(o, BaseClass<Derived>{});
|
||||
s.value1b(o.z);
|
||||
}
|
||||
|
||||
//define relationships between base class and derived classes for runtime polymorphism
|
||||
|
||||
namespace bitsery {
|
||||
namespace ext {
|
||||
|
||||
template<>
|
||||
struct PolymorphicBaseClass<Base> : PolymorphicDerivedClasses<Derived> {
|
||||
};
|
||||
|
||||
template<>
|
||||
struct PolymorphicBaseClass<Derived> : PolymorphicDerivedClasses<MoreDerived> {
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
class SerializeExtensionStdSmartPtrNonPolymorphicType : public testing::Test {
|
||||
public:
|
||||
template<typename U>
|
||||
using TPtr = typename T::template TData<U>;
|
||||
using TExt = typename T::TExt;
|
||||
|
||||
using TContext = std::tuple<PointerLinkingContext>;
|
||||
using SerContext = BasicSerializationContext<bitsery::DefaultConfig, TContext>;
|
||||
|
||||
//this is useful for PolymorphicContext to bind classes to serializer/deserializer
|
||||
using TSerializer = typename SerContext::TSerializer;
|
||||
using TDeserializer = typename SerContext::TDeserializer;
|
||||
|
||||
TContext plctx{};
|
||||
SerContext sctx{};
|
||||
|
||||
typename SerContext::TSerializer &createSerializer() {
|
||||
auto &res = sctx.createSerializer(&plctx);
|
||||
return res;
|
||||
}
|
||||
|
||||
typename SerContext::TDeserializer &createDeserializer() {
|
||||
auto &res = sctx.createDeserializer(&plctx);
|
||||
return res;
|
||||
}
|
||||
|
||||
bool isPointerContextValid() {
|
||||
return std::get<0>(plctx).isValid();
|
||||
}
|
||||
|
||||
virtual void TearDown() override {
|
||||
EXPECT_TRUE(isPointerContextValid());
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class SerializeExtensionStdSmartPtrPolymorphicType : public testing::Test {
|
||||
public:
|
||||
template<typename U>
|
||||
using TPtr = typename T::template TData<U>;
|
||||
using TExt = typename T::TExt;
|
||||
|
||||
using TContext = std::tuple<PointerLinkingContext, InheritanceContext, PolymorphicContext<StandardRTTI>>;
|
||||
using SerContext = BasicSerializationContext<bitsery::DefaultConfig, TContext>;
|
||||
|
||||
//this is useful for PolymorphicContext to bind classes to serializer/deserializer
|
||||
using TSerializer = typename SerContext::TSerializer;
|
||||
using TDeserializer = typename SerContext::TDeserializer;
|
||||
|
||||
TContext plctx{};
|
||||
SerContext sctx{};
|
||||
|
||||
typename SerContext::TSerializer &createSerializer() {
|
||||
auto &res = sctx.createSerializer(&plctx);
|
||||
std::get<2>(plctx).clear();
|
||||
//bind serializer with classes
|
||||
std::get<2>(plctx).template registerBasesList<SerContext::TSerializer>(bitsery::ext::PolymorphicClassesList<Base>{});
|
||||
return res;
|
||||
}
|
||||
|
||||
typename SerContext::TDeserializer &createDeserializer() {
|
||||
auto &res = sctx.createDeserializer(&plctx);
|
||||
std::get<2>(plctx).clear();
|
||||
//bind deserializer with classes
|
||||
std::get<2>(plctx).template registerBasesList<SerContext::TDeserializer>(bitsery::ext::PolymorphicClassesList<Base>{});
|
||||
return res;
|
||||
}
|
||||
|
||||
bool isPointerContextValid() {
|
||||
return std::get<0>(plctx).isValid();
|
||||
}
|
||||
|
||||
virtual void TearDown() override {
|
||||
EXPECT_TRUE(isPointerContextValid());
|
||||
}
|
||||
};
|
||||
|
||||
struct UniquePtrTest {
|
||||
template<typename T>
|
||||
using TData = std::unique_ptr<T>;
|
||||
using TExt = StdSmartPtr;
|
||||
};
|
||||
|
||||
struct SharedPtrTest {
|
||||
template<typename T>
|
||||
using TData = std::shared_ptr<T>;
|
||||
using TExt = StdSmartPtr;
|
||||
};
|
||||
|
||||
using TestingWithNonPolymorphicTypes = ::testing::Types<
|
||||
UniquePtrTest,
|
||||
SharedPtrTest>;
|
||||
|
||||
TYPED_TEST_CASE(SerializeExtensionStdSmartPtrNonPolymorphicType, TestingWithNonPolymorphicTypes);
|
||||
|
||||
using TestingWithPolymorphicTypes = ::testing::Types<
|
||||
UniquePtrTest,
|
||||
SharedPtrTest>;
|
||||
|
||||
TYPED_TEST_CASE(SerializeExtensionStdSmartPtrPolymorphicType, TestingWithPolymorphicTypes);
|
||||
|
||||
TYPED_TEST(SerializeExtensionStdSmartPtrNonPolymorphicType, Data0Result0) {
|
||||
using Ptr = typename TestFixture::template TPtr<MyStruct1>;
|
||||
using Ext = typename TestFixture::TExt;
|
||||
|
||||
Ptr data{};
|
||||
this->createSerializer().ext(data, Ext{});
|
||||
Ptr res{};
|
||||
this->createDeserializer().ext(res, Ext{});
|
||||
|
||||
EXPECT_THAT(data.get(), ::testing::IsNull());
|
||||
EXPECT_THAT(res.get(), ::testing::IsNull());
|
||||
}
|
||||
|
||||
TYPED_TEST(SerializeExtensionStdSmartPtrNonPolymorphicType, Data0Result1) {
|
||||
using Ptr = typename TestFixture::template TPtr<MyStruct1>;
|
||||
using Ext = typename TestFixture::TExt;
|
||||
|
||||
Ptr data{};
|
||||
this->createSerializer().ext(data, Ext{});
|
||||
Ptr res{new MyStruct1{}};
|
||||
this->createDeserializer().ext(res, Ext{});
|
||||
|
||||
EXPECT_THAT(data.get(), ::testing::IsNull());
|
||||
EXPECT_THAT(res.get(), ::testing::IsNull());
|
||||
}
|
||||
|
||||
TYPED_TEST(SerializeExtensionStdSmartPtrNonPolymorphicType, Data1Result0) {
|
||||
using Ptr = typename TestFixture::template TPtr<MyStruct1>;
|
||||
using Ext = typename TestFixture::TExt;
|
||||
|
||||
Ptr data{new MyStruct1{3, 78}};
|
||||
this->createSerializer().ext(data, Ext{});
|
||||
Ptr res{};
|
||||
this->createDeserializer().ext(res, Ext{});
|
||||
|
||||
EXPECT_THAT(data.get(), ::testing::NotNull());
|
||||
EXPECT_THAT(res.get(), ::testing::NotNull());
|
||||
EXPECT_THAT(res->i1, Eq(data->i1));
|
||||
EXPECT_THAT(res->i2, Eq(data->i2));
|
||||
}
|
||||
|
||||
TYPED_TEST(SerializeExtensionStdSmartPtrNonPolymorphicType, Data1Result1) {
|
||||
using Ptr = typename TestFixture::template TPtr<MyStruct1>;
|
||||
using Ext = typename TestFixture::TExt;
|
||||
|
||||
Ptr data{new MyStruct1{3, 78}};
|
||||
this->createSerializer().ext(data, Ext{});
|
||||
Ptr res{new MyStruct1{}};
|
||||
this->createDeserializer().ext(res, Ext{});
|
||||
|
||||
EXPECT_THAT(data.get(), ::testing::NotNull());
|
||||
EXPECT_THAT(res.get(), ::testing::NotNull());
|
||||
EXPECT_THAT(res->i1, Eq(data->i1));
|
||||
EXPECT_THAT(res->i2, Eq(data->i2));
|
||||
}
|
||||
|
||||
TYPED_TEST(SerializeExtensionStdSmartPtrNonPolymorphicType, CanUseLambdaOverload) {
|
||||
using Ptr = typename TestFixture::template TPtr<MyStruct1>;
|
||||
using Ext = typename TestFixture::TExt;
|
||||
|
||||
Ptr data{new MyStruct1{3, 78}};
|
||||
auto &ser = this->createSerializer();
|
||||
ser.ext(data, Ext{}, [&ser](MyStruct1 &o) {
|
||||
//serialize only one field
|
||||
ser.value4b(o.i1);
|
||||
});
|
||||
Ptr res{new MyStruct1{97, 12}};
|
||||
auto &des = this->createDeserializer();
|
||||
des.ext(res, Ext{}, [&des](MyStruct1 &o) {
|
||||
des.value4b(o.i1);
|
||||
});
|
||||
|
||||
EXPECT_THAT(res->i1, Eq(data->i1));
|
||||
EXPECT_THAT(res->i2, Ne(data->i2));
|
||||
}
|
||||
|
||||
TYPED_TEST(SerializeExtensionStdSmartPtrNonPolymorphicType, CanUseValueOverload) {
|
||||
using Ptr = typename TestFixture::template TPtr<uint16_t>;
|
||||
using Ext = typename TestFixture::TExt;
|
||||
|
||||
Ptr data{new uint16_t{3}};
|
||||
this->createSerializer().ext2b(data, Ext{});
|
||||
Ptr res{};
|
||||
this->createDeserializer().ext2b(res, Ext{});
|
||||
EXPECT_THAT(*res, Eq(*data));
|
||||
}
|
||||
|
||||
TYPED_TEST(SerializeExtensionStdSmartPtrNonPolymorphicType, FirstPtrThenPointerObserver) {
|
||||
using Ptr = typename TestFixture::template TPtr<uint16_t>;
|
||||
using Ext = typename TestFixture::TExt;
|
||||
|
||||
Ptr data{new uint16_t{3}};
|
||||
uint16_t *dataObs = data.get();
|
||||
auto &ser = this->createSerializer();
|
||||
ser.ext2b(data, Ext{});
|
||||
ser.ext2b(dataObs, PointerObserver{});
|
||||
Ptr res{};
|
||||
uint16_t *resObs = nullptr;
|
||||
auto &des = this->createDeserializer();
|
||||
des.ext2b(res, Ext{});
|
||||
des.ext2b(resObs, PointerObserver{});
|
||||
|
||||
EXPECT_THAT(resObs, Eq(res.get()));
|
||||
}
|
||||
|
||||
TYPED_TEST(SerializeExtensionStdSmartPtrNonPolymorphicType, FirstPointerObserverThenPtr) {
|
||||
using Ptr = typename TestFixture::template TPtr<uint16_t>;
|
||||
using Ext = typename TestFixture::TExt;
|
||||
|
||||
Ptr data{new uint16_t{3}};
|
||||
uint16_t *dataObs = data.get();
|
||||
auto &ser = this->createSerializer();
|
||||
ser.ext2b(dataObs, PointerObserver{});
|
||||
ser.ext2b(data, Ext{});
|
||||
Ptr res{};
|
||||
uint16_t *resObs = nullptr;
|
||||
auto &des = this->createDeserializer();
|
||||
des.ext2b(resObs, PointerObserver{});
|
||||
des.ext2b(res, Ext{});
|
||||
EXPECT_THAT(resObs, Eq(res.get()));
|
||||
}
|
||||
|
||||
TYPED_TEST(SerializeExtensionStdSmartPtrPolymorphicType, Data0Result0) {
|
||||
using Ptr = typename TestFixture::template TPtr<Base>;
|
||||
using Ext = typename TestFixture::TExt;
|
||||
|
||||
Ptr baseData{};
|
||||
this->createSerializer().ext(baseData, Ext{});
|
||||
Ptr baseRes{};
|
||||
this->createDeserializer().ext(baseRes, Ext{});
|
||||
|
||||
EXPECT_THAT(baseRes.get(), ::testing::IsNull());
|
||||
EXPECT_THAT(baseData.get(), ::testing::IsNull());
|
||||
}
|
||||
|
||||
TYPED_TEST(SerializeExtensionStdSmartPtrPolymorphicType, Data0Result1) {
|
||||
using Ptr = typename TestFixture::template TPtr<Base>;
|
||||
using Ext = typename TestFixture::TExt;
|
||||
|
||||
Ptr baseData{};
|
||||
this->createSerializer().ext(baseData, Ext{});
|
||||
|
||||
Ptr baseRes{new Derived{}};
|
||||
this->createDeserializer().ext(baseRes, Ext{});
|
||||
|
||||
EXPECT_THAT(baseRes.get(), ::testing::IsNull());
|
||||
EXPECT_THAT(baseData.get(), ::testing::IsNull());
|
||||
}
|
||||
|
||||
TYPED_TEST(SerializeExtensionStdSmartPtrPolymorphicType, Data1Result0) {
|
||||
using Ptr = typename TestFixture::template TPtr<Base>;
|
||||
using Ext = typename TestFixture::TExt;
|
||||
|
||||
Ptr baseData{new Derived{3, 78}};
|
||||
this->createSerializer().ext(baseData, Ext{});
|
||||
Ptr baseRes{};
|
||||
this->createDeserializer().ext(baseRes, Ext{});
|
||||
|
||||
auto *data = dynamic_cast<Derived *>(baseData.get());
|
||||
auto *res = dynamic_cast<Derived *>(baseRes.get());
|
||||
|
||||
EXPECT_THAT(data, ::testing::NotNull());
|
||||
EXPECT_THAT(res, ::testing::NotNull());
|
||||
EXPECT_THAT(res->x, Eq(data->x));
|
||||
EXPECT_THAT(res->y, Eq(data->y));
|
||||
}
|
||||
|
||||
TYPED_TEST(SerializeExtensionStdSmartPtrPolymorphicType, DataAndResultWithDifferentRuntimeTypes) {
|
||||
using Ptr = typename TestFixture::template TPtr<Base>;
|
||||
using Ext = typename TestFixture::TExt;
|
||||
|
||||
Ptr baseData{new Derived{3, 78}};
|
||||
this->createSerializer().ext(baseData, Ext{});
|
||||
Ptr baseRes{new Base{}};
|
||||
this->createDeserializer().ext(baseRes, Ext{});
|
||||
|
||||
auto *data = dynamic_cast<Derived *>(baseData.get());
|
||||
auto *res = dynamic_cast<Derived *>(baseRes.get());
|
||||
|
||||
EXPECT_THAT(data, ::testing::NotNull());
|
||||
EXPECT_THAT(res, ::testing::NotNull());
|
||||
EXPECT_THAT(res->x, Eq(data->x));
|
||||
EXPECT_THAT(res->y, Eq(data->y));
|
||||
}
|
||||
|
||||
class SerializeExtensionStdSmartSharedPtr : public testing::Test {
|
||||
public:
|
||||
|
||||
using TContext = std::tuple<PointerLinkingContext, InheritanceContext, PolymorphicContext<StandardRTTI>>;
|
||||
using SerContext = BasicSerializationContext<bitsery::DefaultConfig, TContext>;
|
||||
|
||||
//this is useful for PolymorphicContext to bind classes to serializer/deserializer
|
||||
using TSerializer = typename SerContext::TSerializer;
|
||||
using TDeserializer = typename SerContext::TDeserializer;
|
||||
|
||||
TContext plctx{};
|
||||
SerContext sctx{};
|
||||
|
||||
typename SerContext::TSerializer &createSerializer() {
|
||||
auto &res = sctx.createSerializer(&plctx);
|
||||
std::get<2>(plctx).clear();
|
||||
//bind serializer with classes
|
||||
std::get<2>(plctx).registerBasesList<SerContext::TSerializer>(bitsery::ext::PolymorphicClassesList<Base>{});
|
||||
return res;
|
||||
}
|
||||
|
||||
typename SerContext::TDeserializer &createDeserializer() {
|
||||
auto &res = sctx.createDeserializer(&plctx);
|
||||
std::get<2>(plctx).clear();
|
||||
//bind deserializer with classes
|
||||
std::get<2>(plctx).registerBasesList<SerContext::TDeserializer>(bitsery::ext::PolymorphicClassesList<Base>{});
|
||||
return res;
|
||||
}
|
||||
|
||||
size_t getBufferSize() const {
|
||||
return sctx.getBufferSize();
|
||||
}
|
||||
|
||||
bool isPointerContextValid() {
|
||||
return std::get<0>(plctx).isValid();
|
||||
}
|
||||
|
||||
void clearSharedState() {
|
||||
return std::get<0>(plctx).clearSharedState();
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(SerializeExtensionStdSmartSharedPtr, SameSharedObjectIsSerializedOnce) {
|
||||
|
||||
std::shared_ptr<Base> baseData1{new Derived{3, 78}};
|
||||
std::shared_ptr<Base> baseData2{baseData1};
|
||||
auto &ser = createSerializer();
|
||||
ser.ext(baseData1, StdSmartPtr{});
|
||||
ser.ext(baseData1, StdSmartPtr{});
|
||||
auto &des = createDeserializer();
|
||||
|
||||
//1b linking context (for 1st time)
|
||||
//1b dynamic type info
|
||||
//2b Derived object
|
||||
//1b linking context (for 2nd time)
|
||||
EXPECT_THAT(getBufferSize(), Eq(5));
|
||||
EXPECT_TRUE(isPointerContextValid());
|
||||
}
|
||||
|
||||
TEST_F(SerializeExtensionStdSmartSharedPtr, PointerLinkingContextCorrectlyClearSharedState) {
|
||||
|
||||
std::shared_ptr<Base> baseData1{new Derived{3, 78}};
|
||||
|
||||
auto &ser = createSerializer();
|
||||
ser.ext(baseData1, StdSmartPtr{});
|
||||
std::shared_ptr<Base> baseRes1{};
|
||||
auto &des = createDeserializer();
|
||||
des.ext(baseRes1, StdSmartPtr{});
|
||||
EXPECT_THAT(baseRes1.use_count(), Eq(2));
|
||||
clearSharedState();
|
||||
EXPECT_THAT(baseRes1.use_count(), Eq(1));
|
||||
EXPECT_TRUE(isPointerContextValid());
|
||||
}
|
||||
|
||||
TEST_F(SerializeExtensionStdSmartSharedPtr, CorrectlyManagesSameSharedObject) {
|
||||
|
||||
std::shared_ptr<Base> baseData1{new Derived{3, 78}};
|
||||
std::shared_ptr<Base> baseData2{new Derived{55, 11}};
|
||||
std::shared_ptr<Base> baseData21{baseData2};
|
||||
auto &ser = createSerializer();
|
||||
ser.ext(baseData1, StdSmartPtr{});
|
||||
ser.ext(baseData2, StdSmartPtr{});
|
||||
ser.ext(baseData21, StdSmartPtr{});
|
||||
|
||||
std::shared_ptr<Base> baseRes1{};
|
||||
std::shared_ptr<Base> baseRes2{};
|
||||
std::shared_ptr<Base> baseRes21{};
|
||||
auto &des = createDeserializer();
|
||||
des.ext(baseRes1, StdSmartPtr{});
|
||||
des.ext(baseRes2, StdSmartPtr{});
|
||||
des.ext(baseRes21, StdSmartPtr{});
|
||||
|
||||
auto *data = dynamic_cast<Derived *>(baseRes1.get());
|
||||
EXPECT_THAT(data, ::testing::NotNull());
|
||||
|
||||
clearSharedState();
|
||||
|
||||
EXPECT_THAT(baseRes1.use_count(), Eq(1));
|
||||
EXPECT_THAT(baseRes2.use_count(), Eq(2));
|
||||
EXPECT_THAT(baseRes21.use_count(), Eq(2));
|
||||
baseRes2.reset();
|
||||
EXPECT_THAT(baseRes21.use_count(), Eq(1));
|
||||
EXPECT_TRUE(isPointerContextValid());
|
||||
}
|
||||
|
||||
TEST_F(SerializeExtensionStdSmartSharedPtr, FirstSharedThenWeakPtr) {
|
||||
|
||||
std::shared_ptr<Base> baseData1{new Derived{3, 78}};
|
||||
std::weak_ptr<Base> baseData11{baseData1};
|
||||
std::weak_ptr<Base> baseData12{baseData11};
|
||||
auto &ser = createSerializer();
|
||||
ser.ext(baseData1, StdSmartPtr{});
|
||||
ser.ext(baseData11, StdSmartPtr{});
|
||||
ser.ext(baseData12, StdSmartPtr{});
|
||||
|
||||
std::shared_ptr<Base> baseRes1{};
|
||||
std::weak_ptr<Base> baseRes11{};
|
||||
std::weak_ptr<Base> baseRes12{};
|
||||
auto &des = createDeserializer();
|
||||
des.ext(baseRes1, StdSmartPtr{});
|
||||
des.ext(baseRes11, StdSmartPtr{});
|
||||
des.ext(baseRes12, StdSmartPtr{});
|
||||
|
||||
auto *data = dynamic_cast<Derived *>(baseRes1.get());
|
||||
EXPECT_THAT(data, ::testing::NotNull());
|
||||
|
||||
clearSharedState();
|
||||
|
||||
EXPECT_THAT(baseRes1.use_count(), Eq(1));
|
||||
EXPECT_THAT(baseRes11.use_count(), Eq(1));
|
||||
EXPECT_THAT(baseRes12.use_count(), Eq(1));
|
||||
baseRes1.reset();
|
||||
EXPECT_THAT(baseRes11.use_count(), Eq(0));
|
||||
EXPECT_TRUE(isPointerContextValid());
|
||||
}
|
||||
|
||||
TEST_F(SerializeExtensionStdSmartSharedPtr, FirstWeakThenSharedPtr) {
|
||||
|
||||
std::shared_ptr<Base> baseData1{new Derived{3, 78}};
|
||||
std::weak_ptr<Base> baseData11{baseData1};
|
||||
std::weak_ptr<Base> baseData2{};
|
||||
auto &ser = createSerializer();
|
||||
ser.ext(baseData2, StdSmartPtr{});
|
||||
ser.ext(baseData11, StdSmartPtr{});
|
||||
ser.ext(baseData1, StdSmartPtr{});
|
||||
|
||||
std::shared_ptr<Base> baseRes1{};
|
||||
std::weak_ptr<Base> baseRes11{};
|
||||
std::weak_ptr<Base> baseRes2{};
|
||||
auto &des = createDeserializer();
|
||||
des.ext(baseRes2, StdSmartPtr{});
|
||||
des.ext(baseRes11, StdSmartPtr{});
|
||||
des.ext(baseRes1, StdSmartPtr{});
|
||||
|
||||
auto *data = dynamic_cast<Derived *>(baseRes1.get());
|
||||
EXPECT_THAT(data, ::testing::NotNull());
|
||||
|
||||
clearSharedState();
|
||||
|
||||
EXPECT_THAT(baseRes1.use_count(), Eq(1));
|
||||
EXPECT_THAT(baseRes2.use_count(), Eq(0));
|
||||
EXPECT_THAT(baseRes11.use_count(), Eq(1));
|
||||
baseRes1.reset();
|
||||
EXPECT_THAT(baseRes11.use_count(), Eq(0));
|
||||
EXPECT_TRUE(isPointerContextValid());
|
||||
}
|
||||
|
||||
TEST_F(SerializeExtensionStdSmartSharedPtr, FewPtrsAreEmpty) {
|
||||
|
||||
std::shared_ptr<Base> baseData1{new Derived{3, 78}};
|
||||
std::shared_ptr<Base> baseData2{};
|
||||
std::weak_ptr<Base> baseData3{};
|
||||
std::weak_ptr<Base> baseData11{baseData1};
|
||||
auto &ser = createSerializer();
|
||||
ser.ext(baseData1, StdSmartPtr{});
|
||||
ser.ext(baseData2, StdSmartPtr{});
|
||||
ser.ext(baseData3, StdSmartPtr{});
|
||||
ser.ext(baseData11, StdSmartPtr{});
|
||||
|
||||
std::shared_ptr<Base> baseRes1{};
|
||||
std::shared_ptr<Base> baseRes2{new Derived{3, 78}};
|
||||
std::weak_ptr<Base> baseRes3{baseRes2};
|
||||
std::weak_ptr<Base> baseRes11{};
|
||||
auto &des = createDeserializer();
|
||||
des.ext(baseRes1, StdSmartPtr{});
|
||||
des.ext(baseRes2, StdSmartPtr{});
|
||||
des.ext(baseRes3, StdSmartPtr{});
|
||||
des.ext(baseRes11, StdSmartPtr{});
|
||||
|
||||
clearSharedState();
|
||||
|
||||
EXPECT_THAT(baseRes1.use_count(), Eq(1));
|
||||
EXPECT_THAT(baseRes2.use_count(), Eq(0));
|
||||
EXPECT_THAT(baseRes3.use_count(), Eq(0));
|
||||
EXPECT_THAT(baseRes11.use_count(), Eq(1));
|
||||
baseRes1.reset();
|
||||
EXPECT_THAT(baseRes11.use_count(), Eq(0));
|
||||
EXPECT_TRUE(isPointerContextValid());
|
||||
}
|
||||
|
||||
TEST_F(SerializeExtensionStdSmartSharedPtr, WhenResultObjectExistsSameType) {
|
||||
|
||||
std::shared_ptr<Base> baseData1{new Derived{3, 78}};
|
||||
auto &ser = createSerializer();
|
||||
ser.ext(baseData1, StdSmartPtr{});
|
||||
|
||||
std::shared_ptr<Base> baseRes1{new Derived{0, 0}};
|
||||
auto &des = createDeserializer();
|
||||
des.ext(baseRes1, StdSmartPtr{});
|
||||
|
||||
clearSharedState();
|
||||
|
||||
EXPECT_THAT(baseRes1.use_count(), Eq(1));
|
||||
EXPECT_THAT(baseRes1->x, Eq(baseData1->x));
|
||||
EXPECT_TRUE(isPointerContextValid());
|
||||
}
|
||||
|
||||
TEST_F(SerializeExtensionStdSmartSharedPtr, WhenResultObjectExistsDifferentType) {
|
||||
|
||||
std::shared_ptr<Base> baseData1{new Derived{3, 78}};
|
||||
auto &ser = createSerializer();
|
||||
ser.ext(baseData1, StdSmartPtr{});
|
||||
|
||||
std::shared_ptr<Base> baseRes1{new Base{}};
|
||||
auto &des = createDeserializer();
|
||||
des.ext(baseRes1, StdSmartPtr{});
|
||||
|
||||
clearSharedState();
|
||||
|
||||
EXPECT_THAT(baseRes1.use_count(), Eq(1));
|
||||
EXPECT_THAT(baseRes1->x, Eq(baseData1->x));
|
||||
EXPECT_THAT(dynamic_cast<Derived *>(baseRes1.get()), ::testing::NotNull());
|
||||
EXPECT_TRUE(isPointerContextValid());
|
||||
}
|
||||
|
||||
TEST_F(SerializeExtensionStdSmartSharedPtr, WhenOnlyWeakPtrIsSerializedThenPointerCointextIsInvalid) {
|
||||
std::shared_ptr<Base> tmp{new Derived{3, 78}};
|
||||
std::weak_ptr<Base> baseData1{tmp};
|
||||
auto &ser = createSerializer();
|
||||
ser.ext(baseData1, StdSmartPtr{});
|
||||
|
||||
EXPECT_FALSE(isPointerContextValid());
|
||||
}
|
||||
|
||||
TEST_F(SerializeExtensionStdSmartSharedPtr, WhenOnlyWeakPtrIsDeserializedThenPointerCointextIsInvalid) {
|
||||
std::shared_ptr<Base> baseData1{new Derived{3, 78}};
|
||||
auto &ser = createSerializer();
|
||||
ser.ext(baseData1, StdSmartPtr{});
|
||||
|
||||
std::weak_ptr<Base> baseRes1{};
|
||||
auto &des = createDeserializer();
|
||||
des.ext(baseRes1, StdSmartPtr{});
|
||||
|
||||
EXPECT_FALSE(isPointerContextValid());
|
||||
|
||||
EXPECT_THAT(baseRes1.use_count(), Eq(1));
|
||||
clearSharedState();
|
||||
EXPECT_THAT(baseRes1.use_count(), Eq(0));
|
||||
|
||||
}
|
||||
|
||||
struct TestSharedFromThis : public std::enable_shared_from_this<TestSharedFromThis> {
|
||||
float x{};
|
||||
explicit TestSharedFromThis(): std::enable_shared_from_this<TestSharedFromThis>() {}
|
||||
|
||||
template<typename S>
|
||||
void serialize(S &s) {
|
||||
s.value4b(x);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(SerializeExtensionStdSmartSharedPtr, EnableSharedFromThis) {
|
||||
std::shared_ptr<TestSharedFromThis> dataPtr(new TestSharedFromThis{});
|
||||
std::shared_ptr<TestSharedFromThis> resPtr{};
|
||||
createSerializer().ext(dataPtr, StdSmartPtr{});
|
||||
createDeserializer().ext(resPtr, StdSmartPtr{});
|
||||
clearSharedState();
|
||||
auto resPtr2 = resPtr->shared_from_this();
|
||||
EXPECT_THAT(resPtr->x, Eq(dataPtr->x));
|
||||
EXPECT_THAT(resPtr2.use_count(), Eq(2));
|
||||
}
|
||||
|
||||
struct CustomDeleter {
|
||||
void operator()(Base*p) {
|
||||
delete p;
|
||||
}
|
||||
};
|
||||
class SerializeExtensionStdSmartUniquePtr:public SerializeExtensionStdSmartSharedPtr{};
|
||||
|
||||
TEST_F(SerializeExtensionStdSmartUniquePtr, WithCustomDeleter) {
|
||||
std::unique_ptr<Base, CustomDeleter> dataPtr(new Derived{87,7});
|
||||
std::unique_ptr<Base, CustomDeleter> resPtr{};
|
||||
createSerializer().ext(dataPtr, StdSmartPtr{});
|
||||
createDeserializer().ext(resPtr, StdSmartPtr{});
|
||||
clearSharedState();
|
||||
EXPECT_THAT(resPtr->x, Eq(dataPtr->x));
|
||||
EXPECT_THAT(dynamic_cast<Derived *>(resPtr.get()), ::testing::NotNull());
|
||||
}
|
||||
@@ -20,9 +20,10 @@
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
#include <bitsery/ext/std_stack.h>
|
||||
#include <gmock/gmock.h>
|
||||
#include "serialization_test_utils.h"
|
||||
#include <bitsery/ext/std_stack.h>
|
||||
|
||||
|
||||
using StdStack = bitsery::ext::StdStack;
|
||||
|
||||
|
||||
@@ -21,9 +21,10 @@
|
||||
//SOFTWARE.
|
||||
|
||||
|
||||
#include <bitsery/ext/value_range.h>
|
||||
#include <gmock/gmock.h>
|
||||
#include "serialization_test_utils.h"
|
||||
#include <bitsery/ext/value_range.h>
|
||||
|
||||
using namespace testing;
|
||||
using bitsery::details::RangeSpec;
|
||||
using bitsery::ext::BitsConstraint;
|
||||
|
||||
@@ -21,11 +21,12 @@
|
||||
//SOFTWARE.
|
||||
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include "serialization_test_utils.h"
|
||||
#include <bitsery/traits/string.h>
|
||||
#include <bitsery/traits/array.h>
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include "serialization_test_utils.h"
|
||||
|
||||
using testing::Eq;
|
||||
using testing::StrEq;
|
||||
using testing::ContainerEq;
|
||||
|
||||
@@ -37,26 +37,26 @@ bool SerializeDeserializeContainerSize(SerializationContext& ctx, const size_t s
|
||||
TEST(SerializeSize, WhenLengthLessThan128Then1Byte) {
|
||||
SerializationContext ctx1{};
|
||||
EXPECT_TRUE(SerializeDeserializeContainerSize(ctx1, 127));
|
||||
EXPECT_THAT(ctx1.getBufferSize(), Eq(1));
|
||||
EXPECT_THAT(ctx1.getBufferSize(), Eq(1u));
|
||||
SerializationContext ctx2;
|
||||
EXPECT_TRUE(SerializeDeserializeContainerSize(ctx2, 128));
|
||||
EXPECT_THAT(ctx2.getBufferSize(), testing::Gt(1));
|
||||
EXPECT_THAT(ctx2.getBufferSize(), testing::Gt(1u));
|
||||
}
|
||||
|
||||
TEST(SerializeSize, WhenLengthLessThan16384Then2Bytes) {
|
||||
SerializationContext ctx1;
|
||||
EXPECT_TRUE(SerializeDeserializeContainerSize(ctx1, 16383));
|
||||
EXPECT_THAT(ctx1.getBufferSize(), Eq(2));
|
||||
EXPECT_THAT(ctx1.getBufferSize(), Eq(2u));
|
||||
SerializationContext ctx2;
|
||||
EXPECT_TRUE(SerializeDeserializeContainerSize(ctx2, 16384));
|
||||
EXPECT_THAT(ctx2.getBufferSize(), testing::Gt(2));
|
||||
EXPECT_THAT(ctx2.getBufferSize(), testing::Gt(2u));
|
||||
}
|
||||
|
||||
TEST(SerializeSize, WhenGreaterThan16383Then4Bytes) {
|
||||
SerializationContext ctx1;
|
||||
EXPECT_TRUE(SerializeDeserializeContainerSize(ctx1, 16384));
|
||||
EXPECT_THAT(ctx1.getBufferSize(), Eq(4));
|
||||
EXPECT_THAT(ctx1.getBufferSize(), Eq(4u));
|
||||
SerializationContext ctx2;
|
||||
EXPECT_TRUE(SerializeDeserializeContainerSize(ctx2, 66384));
|
||||
EXPECT_THAT(ctx2.getBufferSize(), Eq(4));
|
||||
}
|
||||
EXPECT_THAT(ctx2.getBufferSize(), Eq(4u));
|
||||
}
|
||||
|
||||
@@ -21,9 +21,9 @@
|
||||
//SOFTWARE.
|
||||
|
||||
|
||||
#include <bitsery/traits/string.h>
|
||||
#include <gmock/gmock.h>
|
||||
#include "serialization_test_utils.h"
|
||||
#include <bitsery/traits/string.h>
|
||||
|
||||
using namespace testing;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user