10 Commits

Author SHA1 Message Date
Mindaugas
6c3e1aee43 removed anonymous namespace from PolymorphicBaseClass as it only works
on clang, and is not standard compliant
2019-01-08 16:00:02 +02:00
Mindaugas
e5f8d5742f Merge branch 'master' of https://github.com/fraillt/bitsery 2019-01-08 15:08:27 +02:00
Mindaugas
a2ecf8d7b0 polymorphism improvements and new CompactValue extension 2019-01-08 15:06:29 +02:00
Mindaugas Vinkelis
670130397b Merge pull request #6 from AJIOB/master
VS 2017.5.6 example project compilation fix
2018-09-17 09:47:36 +03:00
AJIOB
4a0b3cae98 VS 2017.5.6 example compilation fix 2018-09-15 08:37:56 +03:00
Mindaugas Vinkelis
b3b32ab393 Merge pull request #5 from YarikTH/patch-1
Update smart_pointers_with_polymorphism.cpp
2018-08-27 07:24:22 +03:00
Yaroslav
6ebdb9915b Update smart_pointers_with_polymorphism.cpp
Fix Color::operator == in smart_pointers_with_polymorphism example
2018-08-24 10:09:17 +03:00
Mindaugas
2e62bd08e3 cleanup 2018-08-23 14:57:48 +03:00
Mindaugas
54f69a5eea polymorphism and smart pointers 2018-08-23 14:44:58 +03:00
Mindaugas Vinkelis
275c4138ee polymorphism in progress 2018-08-20 13:10:10 +03:00
50 changed files with 2749 additions and 784 deletions

View File

@@ -1,3 +1,69 @@
# [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 +172,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**.

View File

@@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.1)
project(bitsery
LANGUAGES CXX
VERSION 4.2.1)
VERSION 4.4.0)
#======== 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")

View File

@@ -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

View File

@@ -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).

View File

@@ -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)

View File

@@ -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()

View File

@@ -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{};

View File

@@ -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>

View 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;
}

View File

@@ -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,14 @@
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;
BufferIterators(TIterator begin, TIterator end)
:posIt{begin},
endIt{end}
{
: posIt{begin},
endIt{end} {
}
friend details::SessionAccess;
@@ -47,26 +45,26 @@ 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");
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");
InputBufferAdapter(TIterator begin, TIterator end): BufferIterators<Buffer>(begin, end)
{
InputBufferAdapter(TIterator begin, TIterator end)
: BufferIterators<Buffer>(begin, end) {
}
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 +74,6 @@ namespace bitsery {
this->posIt -= size;
//set everything to zeros
std::memset(data, 0, size);
if (error() == ReaderError::NoError)
setError(ReaderError::DataOverflow);
}
@@ -102,6 +99,47 @@ namespace bitsery {
}
};
template<typename Buffer>
class UnsafeInputBufferAdapter : 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");
UnsafeInputBufferAdapter(TIterator begin, TIterator end) : BufferIterators<Buffer>(begin, end) {
}
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 +148,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 +174,7 @@ namespace bitsery {
private:
using TResizable = std::integral_constant<bool, traits::ContainerTraits<Buffer>::isResizable>;
Buffer* _buffer;
Buffer *_buffer;
TIterator _outIt{};
TIterator _end{};
@@ -163,7 +201,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 +239,6 @@ namespace bitsery {
}
};
}
#endif //BITSERY_ADAPTER_BUFFER_H

View File

@@ -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>

View File

@@ -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);

View File

@@ -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;

View File

@@ -25,8 +25,8 @@
#define BITSERY_BITSERY_H
#define BITSERY_MAJOR_VERSION 4
#define BITSERY_MINOR_VERSION 2
#define BITSERY_PATCH_VERSION 1
#define BITSERY_MINOR_VERSION 4
#define BITSERY_PATCH_VERSION 0
#define BITSERY_QUOTE_MACRO(name) #name
#define BITSERY_BUILD_VERSION_STR(major,minor, patch) \

View File

@@ -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

View File

@@ -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

View 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);
}
// 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

View File

@@ -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

View File

@@ -25,7 +25,7 @@
#include <cassert>
#include "../details/adapter_utils.h"
//we need this, so we could
//we need this, so we could reserve for non ordered set
#include <unordered_set>
#include "../traits/core/traits.h"

View File

@@ -0,0 +1,128 @@
//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;
static TElement *getPtr(std::unique_ptr<TElement> &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 std::is_same<std::unique_ptr<TElement>, T>::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

View File

@@ -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;

View File

@@ -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,109 @@
#include <unordered_map>
#include <vector>
#include <memory>
#include "polymorphism_utils.h"
#include <algorithm>
#include <cassert>
#include "../../details/adapter_utils.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 +145,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 +160,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,197 +187,192 @@ 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 = new 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 = new 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:
bool isValid() {
return isPointerSerializationValid() && isPointerDeserializationValid();

View File

@@ -1,225 +1,237 @@
//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"
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(new 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

View File

@@ -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;
}
};
}
}

View 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

View File

@@ -73,7 +73,7 @@ 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 TValue = typename ContainerTraits<T>::TValue;

View File

@@ -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
View 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

View File

@@ -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)

View File

@@ -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

View File

@@ -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;

View File

@@ -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;

View File

@@ -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>;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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),);

View 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));
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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));
}

View File

@@ -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));
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -0,0 +1,672 @@
//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{};
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));
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;