mirror of
https://github.com/fraillt/bitsery.git
synced 2026-06-08 00:03:54 +00:00
polymorphism in progress
This commit is contained in:
committed by
Mindaugas
parent
1ca45aab79
commit
275c4138ee
@@ -1,3 +1,9 @@
|
||||
# [4.2.2]
|
||||
|
||||
### Improvements
|
||||
* improved serialization/deserialization performance for buffer adapters up to ~20%.
|
||||
* new **UnsafeInputBufferAdapter** doesn't check for buffer size on deserialization, can improve deserialization performance up to ~50%.
|
||||
|
||||
# [4.2.1](https://github.com/fraillt/bitsery/compare/v4.2.0...v4.2.1) (2018-03-09)
|
||||
|
||||
### Improvements
|
||||
|
||||
@@ -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")
|
||||
|
||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 Mindaugas Vinkelis
|
||||
Copyright (c) 2018 Mindaugas Vinkelis
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -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{};
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
247
examples/pointers_with_polymorphism.cpp
Normal file
247
examples/pointers_with_polymorphism.cpp
Normal file
@@ -0,0 +1,247 @@
|
||||
//
|
||||
// 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/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::PointerOwner;
|
||||
|
||||
using bitsery::ext::PointerType;
|
||||
|
||||
//define our data structures
|
||||
struct Color {
|
||||
float r, g, b;
|
||||
};
|
||||
|
||||
struct Shape {
|
||||
Color clr;
|
||||
|
||||
virtual ~Shape() = 0;
|
||||
};
|
||||
|
||||
Shape::~Shape() = default;
|
||||
|
||||
struct Circle : Shape {
|
||||
int32_t radius;
|
||||
};
|
||||
|
||||
struct Rectangle : Shape {
|
||||
int32_t width;
|
||||
int32_t height;
|
||||
};
|
||||
|
||||
struct RoundedRectangle : Rectangle {
|
||||
int32_t radius;
|
||||
};
|
||||
|
||||
struct SomeShapes {
|
||||
std::unique_ptr<Shape> main;
|
||||
std::vector<Shape *> list;
|
||||
};
|
||||
|
||||
//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);
|
||||
}
|
||||
|
||||
template<typename S>
|
||||
void serialize(S &s, SomeShapes &o) {
|
||||
s.ext(o.main, bitsery::ext::StdUniquePtr{});
|
||||
s.container(o.list, 100, [&s](Shape *(&item)) {
|
||||
s.ext(item, bitsery::ext::PointerOwner{});
|
||||
});
|
||||
}
|
||||
|
||||
// STEP 1
|
||||
// define relationships between base and derived classes
|
||||
|
||||
namespace bitsery {
|
||||
namespace ext {
|
||||
|
||||
template<>
|
||||
struct PolymorphicBaseClass<Shape> : PolymorphicDerivedClasses<Circle, Rectangle> {
|
||||
};
|
||||
|
||||
template<>
|
||||
struct PolymorphicBaseClass<Rectangle> : PolymorphicDerivedClasses<RoundedRectangle> {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
//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>;
|
||||
|
||||
//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.main.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.list.push_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.list.push_back(tmp);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
//checks if deserialized data is equal
|
||||
void assertSameShapes(const SomeShapes &data, const SomeShapes &res) {
|
||||
{
|
||||
auto d = dynamic_cast<RoundedRectangle *>(data.main.get());
|
||||
auto r = dynamic_cast<RoundedRectangle *>(res.main.get());
|
||||
assert(r != nullptr);
|
||||
assert(d->clr.r == r->clr.r);
|
||||
assert(d->clr.g == r->clr.g);
|
||||
assert(d->clr.b == r->clr.b);
|
||||
assert(d->radius == r->radius);
|
||||
assert(d->width == r->width);
|
||||
assert(d->height == r->height);
|
||||
}
|
||||
{
|
||||
auto d = dynamic_cast<Circle *>(data.list[0]);
|
||||
auto r = dynamic_cast<Circle *>(res.list[0]);
|
||||
assert(r != nullptr);
|
||||
assert(d->clr.r == r->clr.r);
|
||||
assert(d->clr.g == r->clr.g);
|
||||
assert(d->clr.b == r->clr.b);
|
||||
assert(d->radius == r->radius);
|
||||
}
|
||||
{
|
||||
auto d = dynamic_cast<Rectangle *>(data.list[1]);
|
||||
auto r = dynamic_cast<Rectangle *>(res.list[1]);
|
||||
assert(r != nullptr);
|
||||
assert(d->clr.r == r->clr.r);
|
||||
assert(d->clr.g == r->clr.g);
|
||||
assert(d->clr.b == r->clr.b);
|
||||
assert(d->width == r->width);
|
||||
assert(d->height == r->height);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int main() {
|
||||
|
||||
auto data = createData();
|
||||
|
||||
//create buffer to store data
|
||||
Buffer buffer{};
|
||||
size_t writtenSize{};
|
||||
{
|
||||
TContext ctx{};
|
||||
MySerializer ser{OutputAdapter{buffer}, &ctx};
|
||||
//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
|
||||
std::get<1>(ctx).registerBasesList(ser, ext::PolymorphicClassesList<Shape>{});
|
||||
//serialize our data
|
||||
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{};
|
||||
MyDeserializer des{InputAdapter{buffer.begin(), writtenSize}, &ctx};
|
||||
//same as in serialization
|
||||
std::get<1>(ctx).registerBasesList(des, ext::PolymorphicClassesList<Shape>{});
|
||||
//serialize our data
|
||||
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());
|
||||
}
|
||||
assertSameShapes(data, res);
|
||||
//delete raw pointers
|
||||
for (auto &s:res.list)
|
||||
delete s;
|
||||
for (auto &s:data.list)
|
||||
delete s;
|
||||
return 0;
|
||||
}
|
||||
@@ -30,15 +30,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;
|
||||
@@ -48,25 +47,52 @@ namespace bitsery {
|
||||
};
|
||||
|
||||
|
||||
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) {
|
||||
template<typename T>
|
||||
void read(T &data) {
|
||||
//for optimization
|
||||
auto tmp = this->posIt;
|
||||
this->posIt += sizeof(T);
|
||||
if (std::distance(this->posIt, this->endIt) >= 0) {
|
||||
data = *reinterpret_cast<const T *>(std::addressof(*tmp));
|
||||
// auto src = std::addressof(*tmp);
|
||||
//// std::memcpy(&data, src, sizeof(T));
|
||||
// switch (sizeof(T)) {
|
||||
// case 1: std::memcpy(&data, src, sizeof(T)); break;
|
||||
// case 2: std::memcpy(&data, src, sizeof(T)); break;
|
||||
// case 4: std::memcpy(&data, src, sizeof(T)); break;
|
||||
// case 8: std::memcpy(&data, src, sizeof(T)); break;
|
||||
// case 16: std::memcpy(&data, src, sizeof(T)); break;
|
||||
// }
|
||||
|
||||
} else {
|
||||
this->posIt -= sizeof(T);
|
||||
data = {};
|
||||
if (error() == ReaderError::NoError)
|
||||
setError(ReaderError::DataOverflow);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void read(TValue *data, size_t size) {
|
||||
//for optimization
|
||||
auto tmp = this->posIt;
|
||||
this->posIt += size;
|
||||
@@ -76,7 +102,6 @@ namespace bitsery {
|
||||
this->posIt -= size;
|
||||
//set everything to zeros
|
||||
std::memset(data, 0, size);
|
||||
|
||||
if (error() == ReaderError::NoError)
|
||||
setError(ReaderError::DataOverflow);
|
||||
}
|
||||
@@ -102,6 +127,58 @@ 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)) {
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void read(T &data) {
|
||||
//for optimization
|
||||
auto tmp = this->posIt;
|
||||
this->posIt += sizeof(T);
|
||||
assert(std::distance(this->posIt, this->endIt) >= 0);
|
||||
data = *reinterpret_cast<const T *>(std::addressof(*tmp));
|
||||
}
|
||||
|
||||
|
||||
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,16 +187,21 @@ 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{});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void write(const T &data) {
|
||||
writeInternal<T>(data, TResizable{});
|
||||
}
|
||||
|
||||
void write(const TValue *data, size_t size) {
|
||||
writeInternal(data, size, TResizable{});
|
||||
@@ -136,7 +218,7 @@ namespace bitsery {
|
||||
private:
|
||||
using TResizable = std::integral_constant<bool, traits::ContainerTraits<Buffer>::isResizable>;
|
||||
|
||||
Buffer* _buffer;
|
||||
Buffer *_buffer;
|
||||
TIterator _outIt{};
|
||||
TIterator _end{};
|
||||
|
||||
@@ -153,6 +235,53 @@ namespace bitsery {
|
||||
_outIt = std::begin(*_buffer);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void writeInternal(const T &data, std::true_type) {
|
||||
//optimization
|
||||
#if defined(_MSC_VER) && (_ITERATOR_DEBUG_LEVEL > 0)
|
||||
using TDistance = typename std::iterator_traits<TIterator>::difference_type;
|
||||
if (std::distance(_outIt , _end) >= static_cast<TDistance>(size)) {
|
||||
*reinterpret_cast<T*>(std::addressof(*tmp)) = data;
|
||||
_outIt += sizeof(T);
|
||||
#else
|
||||
auto tmp = _outIt;
|
||||
_outIt += sizeof(T);
|
||||
if (std::distance(_outIt, _end) >= 0) {
|
||||
*reinterpret_cast<T *>(std::addressof(*tmp)) = data;
|
||||
auto x = reinterpret_cast<T *>(std::addressof(*tmp));
|
||||
*x = data;
|
||||
// auto dst = std::addressof(*tmp);
|
||||
//// std::memcpy(dst, &data, sizeof(T));
|
||||
//
|
||||
// switch (sizeof(T)) {
|
||||
// case 1: std::memcpy(dst, &data, sizeof(T)); break;
|
||||
// case 2: std::memcpy(dst, &data, sizeof(T)); break;
|
||||
// case 4: std::memcpy(dst, &data, sizeof(T)); break;
|
||||
// case 8: std::memcpy(dst, &data, sizeof(T)); break;
|
||||
// case 16: std::memcpy(dst, &data, sizeof(T)); break;
|
||||
// }
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
} else {
|
||||
#if defined(_MSC_VER) && (_ITERATOR_DEBUG_LEVEL > 0)
|
||||
|
||||
#else
|
||||
_outIt -= sizeof(T);
|
||||
#endif
|
||||
//get current position before invalidating iterators
|
||||
const auto pos = std::distance(std::begin(*_buffer), _outIt);
|
||||
//increase container size
|
||||
traits::BufferAdapterTraits<Buffer>::increaseBufferSize(*_buffer);
|
||||
//restore iterators
|
||||
_end = std::end(*_buffer);
|
||||
_outIt = std::next(std::begin(*_buffer), pos);
|
||||
|
||||
writeInternal(data, std::true_type{});
|
||||
}
|
||||
}
|
||||
|
||||
void writeInternal(const TValue *data, const size_t size, std::true_type) {
|
||||
//optimization
|
||||
#if defined(_MSC_VER) && (_ITERATOR_DEBUG_LEVEL > 0)
|
||||
@@ -163,7 +292,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 {
|
||||
@@ -192,6 +321,15 @@ namespace bitsery {
|
||||
_end = std::end(*_buffer);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void writeInternal(const T &data, std::false_type) {
|
||||
//optimization
|
||||
auto tmp = _outIt;
|
||||
_outIt += sizeof(T);
|
||||
assert(std::distance(_outIt, _end) >= 0);
|
||||
*reinterpret_cast<T *>(std::addressof(*tmp)) = data;
|
||||
}
|
||||
|
||||
void writeInternal(const TValue *data, size_t size, std::false_type) {
|
||||
//optimization
|
||||
auto tmp = _outIt;
|
||||
|
||||
@@ -40,6 +40,11 @@ namespace bitsery {
|
||||
BasicInputStreamAdapter(std::basic_ios<TChar, CharTraits>& istream)
|
||||
:_ios{std::addressof(istream)} {}
|
||||
|
||||
template <typename T>
|
||||
void read(T& data) {
|
||||
read(reinterpret_cast<TValue*>(&data), sizeof(T));
|
||||
}
|
||||
|
||||
void read(TValue* data, size_t size) {
|
||||
if (static_cast<size_t>(_ios->rdbuf()->sgetn( data , size )) != size) {
|
||||
*data = {};
|
||||
@@ -80,6 +85,11 @@ namespace bitsery {
|
||||
BasicOutputStreamAdapter(std::basic_ios<TChar, CharTraits>& ostream)
|
||||
:_ios{std::addressof(ostream)} {}
|
||||
|
||||
template <typename T>
|
||||
void write(const T& data) {
|
||||
write(reinterpret_cast<const TValue*>(&data), sizeof(T));
|
||||
}
|
||||
|
||||
void write(const TValue* data, size_t size) {
|
||||
//for optimization
|
||||
_ios->rdbuf()->sputn( data , size );
|
||||
@@ -149,6 +159,28 @@ namespace bitsery {
|
||||
|
||||
~BasicBufferedOutputStreamAdapter() = default;
|
||||
|
||||
template <typename T>
|
||||
void write(const T& data) {
|
||||
auto tmp = _outIt;
|
||||
|
||||
#if defined(_MSC_VER) && (_ITERATOR_DEBUG_LEVEL > 0)
|
||||
using TDistance = typename std::iterator_traits<BufferIt>::difference_type;
|
||||
if (std::distance(_outIt , std::end(_buf)) >= static_cast<TDistance>(size)) {
|
||||
*reinterpret_cast<T*>(std::addressof(*tmp)) = data;
|
||||
_outIt += sizeof(T);
|
||||
#else
|
||||
_outIt += sizeof(T);
|
||||
if (std::distance(_outIt , std::end(_buf)) >= 0) {
|
||||
*reinterpret_cast<T*>(std::addressof(*tmp)) = data;
|
||||
#endif
|
||||
} else {
|
||||
//when buffer is full write out to stream
|
||||
_outIt = std::begin(_buf);
|
||||
_adapter.write(std::addressof(*_outIt), static_cast<size_t>(std::distance(_outIt, tmp)));
|
||||
_adapter.write(reinterpret_cast<const TValue*>(&data), sizeof(T));
|
||||
}
|
||||
}
|
||||
|
||||
void write(const TValue* data, size_t size) {
|
||||
auto tmp = _outIt;
|
||||
|
||||
|
||||
@@ -68,14 +68,17 @@ namespace bitsery {
|
||||
void readBytes(T &v) {
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
directRead(&v, 1);
|
||||
//do something with it, because the way it is implemented in buffer/stream adapter is probably UB,
|
||||
//implementing it in non UB fassion, has no benefit and can be completely remove instead
|
||||
// directReadValue(v, SwapTag{});
|
||||
directReadBuffer(&v,1, SwapTag{});
|
||||
}
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void readBuffer(T *buf, size_t count) {
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
directRead(buf, count);
|
||||
directReadBuffer(buf, count, SwapTag{});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
@@ -115,10 +118,6 @@ namespace bitsery {
|
||||
}
|
||||
}
|
||||
|
||||
const InputAdapter& adapter() const {
|
||||
return _inputAdapter;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class AdapterReaderBitPackingWrapper<AdapterReader<InputAdapter, Config>>;
|
||||
|
||||
@@ -128,23 +127,28 @@ namespace bitsery {
|
||||
session::DisabledSessionsReader<AdapterReader<InputAdapter, Config>>>::type
|
||||
_session;
|
||||
|
||||
template<typename T>
|
||||
void directRead(T *v, size_t count) {
|
||||
static_assert(!std::is_const<T>::value, "");
|
||||
_inputAdapter.read(reinterpret_cast<TValue *>(v), sizeof(T) * count);
|
||||
//swap each byte if nessesarry
|
||||
_swapDataBits(v, count, std::integral_constant<bool,
|
||||
Config::NetworkEndianness != details::getSystemEndianness()>{});
|
||||
using SwapTag = std::integral_constant<bool, Config::NetworkEndianness != details::getSystemEndianness()>;
|
||||
|
||||
template <typename T>
|
||||
void directReadValue(T& v, std::true_type) {
|
||||
_inputAdapter.read(v);
|
||||
v = details::swap(v);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void directReadValue(T& v, std::false_type) {
|
||||
_inputAdapter.read(v);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void _swapDataBits(T *v, size_t count, std::true_type) {
|
||||
void directReadBuffer(T *v, size_t count, std::true_type) {
|
||||
_inputAdapter.read(reinterpret_cast<TValue *>(v), sizeof(T) * count);
|
||||
std::for_each(v, std::next(v, count), [this](T &x) { x = details::swap(x); });
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void _swapDataBits(T *, size_t , std::false_type) {
|
||||
//empty function because no swap is required
|
||||
void directReadBuffer(T *v, size_t count, std::false_type) {
|
||||
_inputAdapter.read(reinterpret_cast<TValue *>(v), sizeof(T) * count);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@@ -135,15 +135,17 @@ namespace bitsery {
|
||||
void writeBytes(const T &v) {
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
directWrite(&v, 1);
|
||||
|
||||
//do something with it, because the way it is implemented in buffer/stream adapter is probably UB,
|
||||
//implementing it in non UB fassion, has no benefit and can be completely remove instead
|
||||
// directWriteValue(v, SwapTag{});
|
||||
directWriteBuffer(&v, 1, SwapTag{});
|
||||
}
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void writeBuffer(const T *buf, size_t count) {
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
directWrite(buf, count);
|
||||
directWriteBuffer(buf, count, SwapTag{});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
@@ -180,14 +182,21 @@ namespace bitsery {
|
||||
|
||||
private:
|
||||
friend class AdapterWriterBitPackingWrapper<AdapterWriter<OutputAdapter, Config>>;
|
||||
template<typename T>
|
||||
void directWrite(T &&v, size_t count) {
|
||||
_directWriteSwapTag(std::forward<T>(v), count, std::integral_constant<bool,
|
||||
Config::NetworkEndianness != details::getSystemEndianness()>{});
|
||||
|
||||
using SwapTag = std::integral_constant<bool, Config::NetworkEndianness != details::getSystemEndianness()>;
|
||||
|
||||
template <typename T>
|
||||
void directWriteValue(const T& v, std::true_type) {
|
||||
_outputAdapter.write(details::swap(v));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void directWriteValue(const T& v, std::false_type) {
|
||||
_outputAdapter.write(v);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void _directWriteSwapTag(const T *v, size_t count, std::true_type) {
|
||||
void directWriteBuffer(const T *v, size_t count, std::true_type) {
|
||||
std::for_each(v, std::next(v, count), [this](const T &v) {
|
||||
const auto res = details::swap(v);
|
||||
_outputAdapter.write(reinterpret_cast<const TValue *>(&res), sizeof(T));
|
||||
@@ -195,7 +204,7 @@ namespace bitsery {
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void _directWriteSwapTag(const T *v, size_t count, std::false_type) {
|
||||
void directWriteBuffer(const T *v, size_t count, std::false_type) {
|
||||
_outputAdapter.write(reinterpret_cast<const TValue *>(v), count * sizeof(T));
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -23,150 +23,139 @@
|
||||
#ifndef BITSERY_EXT_POINTER_H
|
||||
#define BITSERY_EXT_POINTER_H
|
||||
|
||||
#include <cassert>
|
||||
#include "../traits/core/traits.h"
|
||||
#include "inheritance.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& obj, TElement* valuePtr) {}
|
||||
static void clear(T& obj) {}
|
||||
// LCOV_EXCL_STOP
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
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, NoRTTI>;
|
||||
|
||||
|
||||
//inherit from PointerObjectExtensionBase in order to specify PointerType::NotNull
|
||||
class ReferencedByPointer: public pointer_utils::PointerObjectExtensionBase<
|
||||
pointer_details::NonPtrManager, PolymorphicContext, 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, 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 *> {
|
||||
//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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
156
include/bitsery/ext/std_smart_ptr.h
Normal file
156
include/bitsery/ext/std_smart_ptr.h
Normal file
@@ -0,0 +1,156 @@
|
||||
//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.
|
||||
|
||||
#ifndef BITSERY_EXT_STD_SMART_PTR_H
|
||||
#define BITSERY_EXT_STD_SMART_PTR_H
|
||||
|
||||
#include <cassert>
|
||||
#include "../traits/core/traits.h"
|
||||
#include "inheritance.h"
|
||||
#include "utils/pointer_utils.h"
|
||||
#include "utils/polymorphism_utils.h"
|
||||
#include "utils/rtti_utils.h"
|
||||
#include <memory>
|
||||
|
||||
|
||||
#include <iostream>
|
||||
|
||||
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
|
||||
: PointerOwnershipType::Shared;
|
||||
}
|
||||
|
||||
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 StdUniquePtrBase = pointer_utils::PointerObjectExtensionBase<
|
||||
smart_ptr_details::SmartPtrOwnerManager, PolymorphicContext, RTTI>;
|
||||
|
||||
//helper type for convienience
|
||||
using StdUniquePtr = StdUniquePtrBase<StandardRTTI>;
|
||||
|
||||
template <typename RTTI>
|
||||
using StdSharedPtrBase = pointer_utils::PointerObjectExtensionBase<
|
||||
smart_ptr_details::SmartPtrOwnerManager, PolymorphicContext, RTTI>;
|
||||
|
||||
//helper type for convienience
|
||||
using StdSharedPtr = StdSharedPtrBase<StandardRTTI>;
|
||||
|
||||
}
|
||||
|
||||
namespace traits {
|
||||
|
||||
template<typename T, typename RTTI>
|
||||
struct ExtensionTraits<ext::StdUniquePtrBase<RTTI>, std::unique_ptr<T>> {
|
||||
using TValue = T;
|
||||
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>();
|
||||
};
|
||||
|
||||
template<typename T, typename RTTI>
|
||||
struct ExtensionTraits<ext::StdSharedPtrBase<RTTI>, std::shared_ptr<T>> {
|
||||
using TValue = T;
|
||||
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>();
|
||||
};
|
||||
|
||||
template<typename T, typename RTTI>
|
||||
struct ExtensionTraits<ext::StdSharedPtrBase<RTTI>, std::weak_ptr<T>> {
|
||||
using TValue = T;
|
||||
static constexpr bool SupportValueOverload = true;
|
||||
static constexpr bool SupportObjectOverload = true;
|
||||
//if underlying type is not polymorphic, then we can enable lambda syntax
|
||||
static constexpr bool SupportLambdaOverload = !RTTI::template isPolymorphic<TValue>();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif //BITSERY_EXT_STD_SMART_PTR_H
|
||||
@@ -67,9 +67,9 @@ namespace bitsery {
|
||||
}
|
||||
|
||||
namespace traits {
|
||||
template<typename T>
|
||||
struct ExtensionTraits<ext::StdStack, T> {
|
||||
using TValue = typename T::value_type;
|
||||
template<typename T, typename Seq>
|
||||
struct ExtensionTraits<ext::StdStack, std::stack<T, Seq>> {
|
||||
using TValue = T;
|
||||
static constexpr bool SupportValueOverload = true;
|
||||
static constexpr bool SupportObjectOverload = true;
|
||||
static constexpr bool SupportLambdaOverload = true;
|
||||
|
||||
@@ -1,6 +1,25 @@
|
||||
//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,12 +27,14 @@
|
||||
#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
|
||||
@@ -32,7 +53,14 @@ namespace bitsery {
|
||||
//forward declaration
|
||||
class PointerLinkingContext;
|
||||
|
||||
namespace utils {
|
||||
namespace pointer_utils {
|
||||
|
||||
enum SharedSerializationStatus {
|
||||
NotSerialized,
|
||||
SerializedWeak,
|
||||
SerializedShared
|
||||
};
|
||||
|
||||
class PointerLinkingContextSerialization {
|
||||
public:
|
||||
explicit PointerLinkingContextSerialization()
|
||||
@@ -97,8 +125,8 @@ namespace bitsery {
|
||||
};
|
||||
|
||||
//this class is used to store context for shared ptr owners
|
||||
struct PointerSharedContextBase {
|
||||
virtual ~PointerSharedContextBase() = default;
|
||||
struct PointerSharedStateBase {
|
||||
virtual ~PointerSharedStateBase() = default;
|
||||
};
|
||||
|
||||
class PointerLinkingContextDeserialization {
|
||||
@@ -123,7 +151,9 @@ namespace bitsery {
|
||||
ownershipType{ownershipType_},
|
||||
ownerPtr{ptr},
|
||||
observersList{},
|
||||
sharedContext{} {};
|
||||
sharedContext{},
|
||||
sharedCount{}
|
||||
{};
|
||||
|
||||
PointerInfo(const PointerInfo &) = delete;
|
||||
|
||||
@@ -156,21 +186,35 @@ namespace bitsery {
|
||||
PointerOwnershipType ownershipType;
|
||||
void *ownerPtr;
|
||||
std::vector<std::reference_wrapper<void *>> observersList;
|
||||
std::unique_ptr<PointerSharedContextBase> sharedContext;
|
||||
std::unique_ptr<PointerSharedStateBase> sharedContext;
|
||||
size_t sharedCount;
|
||||
};
|
||||
|
||||
PointerInfo &getInfoById(size_t id, PointerOwnershipType ptrType) {
|
||||
auto res = _idMap.emplace(id, PointerInfo{id, nullptr, ptrType});
|
||||
auto &ptrInfo = res.first->second;
|
||||
if (!res.second) {
|
||||
assert(ptrType != PointerOwnershipType::Owner ||
|
||||
ptrInfo.ownershipType == PointerOwnershipType::Observer);
|
||||
if (ptrInfo.ownershipType == PointerOwnershipType::Observer)
|
||||
//id 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++;
|
||||
}
|
||||
return ptrInfo;
|
||||
}
|
||||
|
||||
void clearSharedState() {
|
||||
_idMap.clear();
|
||||
}
|
||||
|
||||
//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) {
|
||||
@@ -182,131 +226,151 @@ namespace bitsery {
|
||||
std::unordered_map<size_t, PointerInfo> _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> typename TPtrManager, template <typename> typename 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.sharedCount == 0)
|
||||
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 shared object, e.g. shared_ptr<Base> and shared_ptr<Derived>
|
||||
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(PointerLinkingContextDeserialization::PointerInfo& ptrInfo, Des &des, T &obj, Fnc &&,
|
||||
Reader &r, std::true_type polymorph, 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(PointerLinkingContextDeserialization::PointerInfo& ptrInfo, Des &, T &obj, Fnc &&fnc,
|
||||
Reader &, std::false_type polymorph, 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(PointerLinkingContextDeserialization::PointerInfo& ptrInfo, Des &des, T &obj, Fnc &&,
|
||||
Reader &r, std::true_type polymorph, std::integral_constant<PointerOwnershipType, PointerOwnershipType::Shared> ) const {
|
||||
auto& sharedState = ptrInfo.sharedContext;
|
||||
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(PointerLinkingContextDeserialization::PointerInfo& ptrInfo, Des &, T &obj, Fnc &&fnc,
|
||||
Reader &, std::false_type polymorph, std::integral_constant<PointerOwnershipType, PointerOwnershipType::Shared>) const {
|
||||
auto& sharedState = ptrInfo.sharedContext;
|
||||
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 isPolymorphic>
|
||||
void deserializeImpl(PointerLinkingContextDeserialization::PointerInfo& ptrInfo, Des &, T &obj, Fnc &&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();
|
||||
|
||||
@@ -1,225 +1,216 @@
|
||||
//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;
|
||||
|
||||
};
|
||||
|
||||
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, typename TBase, typename TDerived>
|
||||
void add() {
|
||||
addToMap<TSerializer, TBase, TDerived>(std::is_abstract<TDerived>{});
|
||||
addChilds<TSerializer, TBase, TDerived>(typename PolymorphicBaseClass<TDerived>::Childs{});
|
||||
}
|
||||
|
||||
template<typename TSerializer, 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, TBase, T1>();
|
||||
addChilds<TSerializer, TBase, TDerived>(PolymorphicClassesList<Tn...>{});
|
||||
//iterate through derived class hierarchy as well
|
||||
add<TSerializer, T1, T1>();
|
||||
}
|
||||
|
||||
template<typename TSerializer, 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, typename T1, typename ...Tn>
|
||||
void registerBasesList(const TSerializer &s, PolymorphicClassesList<T1, Tn...>) {
|
||||
add<TSerializer, T1, T1>();
|
||||
registerBasesList<TSerializer>(s, PolymorphicClassesList<Tn...>{});
|
||||
}
|
||||
|
||||
template<typename TSerializer>
|
||||
void registerBasesList(const TSerializer &, PolymorphicClassesList<>) {
|
||||
}
|
||||
|
||||
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());
|
||||
|
||||
|
||||
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)));
|
||||
};
|
||||
//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);
|
||||
|
||||
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);
|
||||
//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{};
|
||||
handler->process(&des, obj);
|
||||
} else
|
||||
reader.setError(ReaderError::InvalidPointer);
|
||||
};
|
||||
|
||||
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{};
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif //BITSERY_EXT_POLYMORPHISM_H
|
||||
#endif //BITSERY_EXT_POLYMORPHISM_UTILS_H
|
||||
|
||||
@@ -1,27 +1,86 @@
|
||||
//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;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
struct NoRTTI {
|
||||
template<typename TBase>
|
||||
static size_t get(TBase &obj) {
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ set(CTEST_BINARY_DIRECTORY "build")
|
||||
set(ENV{CXXFLAGS} "--coverage")
|
||||
#when using Ninja generator, ctest_coverage cannot find files...
|
||||
set(CTEST_CMAKE_GENERATOR "CodeBlocks - Unix Makefiles")
|
||||
set(CTEST_USE_LAUNCHERS 1)
|
||||
#set(CTEST_USE_LAUNCHERS 1)
|
||||
|
||||
set(CTEST_COVERAGE_COMMAND "gcov")
|
||||
|
||||
|
||||
2
scripts/show_coverage.sh
Normal file → Executable file
2
scripts/show_coverage.sh
Normal file → Executable file
@@ -1,5 +1,5 @@
|
||||
#!/bin/sh
|
||||
BUILD_DIR=$1
|
||||
BUILD_DIR=./build
|
||||
TESTS_BUILD_DIR=$BUILD_DIR/tests/CMakeFiles/
|
||||
COV_INFO=$TESTS_BUILD_DIR/bitsery_coverage.info
|
||||
lcov --directory $TESTS_BUILD_DIR --capture --output-file $COV_INFO
|
||||
|
||||
@@ -20,13 +20,14 @@
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
|
||||
#include <bitsery/adapter/stream.h>
|
||||
#include <bitsery/adapter_writer.h>
|
||||
#include <bitsery/adapter_reader.h>
|
||||
#include <bitsery/traits/vector.h>
|
||||
#include <bitsery/traits/array.h>
|
||||
#include <bitsery/traits/string.h>
|
||||
#include <gmock/gmock.h>
|
||||
#include <sstream>
|
||||
|
||||
//some helper types
|
||||
|
||||
@@ -21,11 +21,11 @@
|
||||
//SOFTWARE.
|
||||
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <bitsery/adapter_writer.h>
|
||||
#include <bitsery/adapter_reader.h>
|
||||
#include <bitsery/ext/value_range.h>
|
||||
#include "serialization_test_utils.h"
|
||||
#include <gmock/gmock.h>
|
||||
|
||||
using testing::Eq;
|
||||
using testing::ContainerEq;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -20,9 +20,11 @@
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
|
||||
#include <bitsery/traits/string.h>
|
||||
#include <gmock/gmock.h>
|
||||
#include "serialization_test_utils.h"
|
||||
#include <bitsery/traits/string.h>
|
||||
|
||||
|
||||
using testing::Eq;
|
||||
using SessionsEnabledWriter = bitsery::AdapterWriter<OutputAdapter, SessionsEnabledConfig>;
|
||||
|
||||
@@ -20,11 +20,12 @@
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include "serialization_test_utils.h"
|
||||
#include <bitsery/details/serialization_common.h>
|
||||
#include <bitsery/traits/array.h>
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include "serialization_test_utils.h"
|
||||
|
||||
using testing::Eq;
|
||||
using testing::ContainerEq;
|
||||
using bitsery::EndiannessType;
|
||||
|
||||
@@ -21,10 +21,7 @@
|
||||
//SOFTWARE.
|
||||
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include "serialization_test_utils.h"
|
||||
#include <bitsery/flexible.h>
|
||||
|
||||
#include <bitsery/flexible/string.h>
|
||||
#include <bitsery/flexible/array.h>
|
||||
#include <bitsery/flexible/vector.h>
|
||||
@@ -38,6 +35,10 @@
|
||||
#include <bitsery/flexible/set.h>
|
||||
#include <bitsery/flexible/unordered_set.h>
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include "serialization_test_utils.h"
|
||||
|
||||
|
||||
using testing::Eq;
|
||||
|
||||
TEST(FlexibleSyntax, FundamentalTypesAndBool) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -21,11 +21,12 @@
|
||||
//SOFTWARE.
|
||||
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include "serialization_test_utils.h"
|
||||
#include <bitsery/ext/entropy.h>
|
||||
#include <bitsery/traits/list.h>
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include "serialization_test_utils.h"
|
||||
|
||||
using namespace testing;
|
||||
|
||||
using bitsery::ext::Entropy;
|
||||
|
||||
@@ -20,9 +20,9 @@
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
#include <bitsery/ext/growable.h>
|
||||
#include <gmock/gmock.h>
|
||||
#include "serialization_test_utils.h"
|
||||
#include <bitsery/ext/growable.h>
|
||||
|
||||
using namespace testing;
|
||||
|
||||
|
||||
@@ -20,9 +20,9 @@
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
#include <bitsery/ext/inheritance.h>
|
||||
#include <gmock/gmock.h>
|
||||
#include "serialization_test_utils.h"
|
||||
#include <bitsery/ext/inheritance.h>
|
||||
|
||||
using bitsery::ext::BaseClass;
|
||||
using bitsery::ext::VirtualBaseClass;
|
||||
|
||||
@@ -20,9 +20,9 @@
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
#include <bitsery/ext/pointer.h>
|
||||
#include <gmock/gmock.h>
|
||||
#include "serialization_test_utils.h"
|
||||
#include <bitsery/ext/pointer.h>
|
||||
|
||||
using bitsery::ext::PointerOwner;
|
||||
using bitsery::ext::PointerObserver;
|
||||
@@ -34,34 +34,34 @@ 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 +71,7 @@ public:
|
||||
};
|
||||
|
||||
TEST(SerializeExtensionPointer, RequiresPointerLinkingContext) {
|
||||
MyStruct1* data = nullptr;
|
||||
MyStruct1 *data = nullptr;
|
||||
//linking context
|
||||
PointerLinkingContext plctx1{};
|
||||
SerContext sctx1;
|
||||
@@ -89,7 +89,7 @@ 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));
|
||||
@@ -99,7 +99,7 @@ TEST(SerializeExtensionPointer, PointerLinkingContextAcceptsMultipleSharedOwners
|
||||
|
||||
TEST_F(SerializeExtensionPointerSerialization, WhenPointersAreNullThenIsValid) {
|
||||
|
||||
auto& ser = createSerializer();
|
||||
auto &ser = createSerializer();
|
||||
ser.ext2b(p1null, PointerOwner{});
|
||||
ser.ext2b(p1null, PointerObserver{});
|
||||
ser.ext(p3null, PointerOwner{});
|
||||
@@ -109,11 +109,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 +123,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 +133,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 +141,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 +156,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 +169,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 +181,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 +194,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 +213,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 +222,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 +242,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 +257,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 +286,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 +297,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 +326,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 +346,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 +354,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{});
|
||||
@@ -374,15 +375,16 @@ TEST_F(SerializeExtensionPointerDeserialization, PointerObserver) {
|
||||
|
||||
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 +392,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 +409,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]);
|
||||
@@ -448,3 +450,80 @@ TEST(SerializeExtensionPointer, IntegrationTest) {
|
||||
delete data.pi1;
|
||||
delete res.pi1;
|
||||
}
|
||||
|
||||
|
||||
TEST(SerializeExtensionPointer, PointerOwnerWithNonPolymorphicTypeCanUseLambdaOverload) {
|
||||
const int32_t NEW_VALUE = 2;
|
||||
const int32_t OLD_VALUE = 1;
|
||||
MyStruct1 *data = new MyStruct1{NEW_VALUE, NEW_VALUE};
|
||||
MyStruct1 *res = new MyStruct1{OLD_VALUE, OLD_VALUE};
|
||||
//linking context
|
||||
PointerLinkingContext plctx1{};
|
||||
SerContext sctx1;
|
||||
auto &ser = sctx1.createSerializer(&plctx1);
|
||||
ser.ext(data, PointerOwner{}, [&ser](MyStruct1 &o) {
|
||||
//serialize only one field
|
||||
ser.value4b(o.i1);
|
||||
});
|
||||
auto &des = sctx1.createDeserializer(&plctx1);
|
||||
des.ext(res, PointerOwner{}, [&des](MyStruct1 &o) {
|
||||
//deserialize only one field
|
||||
des.value4b(o.i1);
|
||||
});
|
||||
|
||||
EXPECT_THAT(res->i1, Eq(NEW_VALUE));
|
||||
EXPECT_THAT(res->i2, Eq(OLD_VALUE));//we didn't serialized that
|
||||
|
||||
delete data;
|
||||
delete res;
|
||||
}
|
||||
|
||||
TEST(SerializeExtensionPointer, ReferencedByPointerCanUseLambdaOverload) {
|
||||
const int32_t NEW_VALUE = 2;
|
||||
const int32_t OLD_VALUE = 1;
|
||||
MyStruct1 data = MyStruct1{NEW_VALUE, NEW_VALUE};
|
||||
MyStruct1 res = MyStruct1{OLD_VALUE, OLD_VALUE};
|
||||
//linking context
|
||||
PointerLinkingContext plctx1{};
|
||||
SerContext sctx1;
|
||||
auto &ser = sctx1.createSerializer(&plctx1);
|
||||
ser.ext(data, ReferencedByPointer{}, [&ser](MyStruct1 &o) {
|
||||
//serialize only one field
|
||||
ser.value4b(o.i1);
|
||||
});
|
||||
auto &des = sctx1.createDeserializer(&plctx1);
|
||||
des.ext(res, ReferencedByPointer{}, [&des](MyStruct1 &o) {
|
||||
//deserialize only one field
|
||||
des.value4b(o.i1);
|
||||
});
|
||||
|
||||
EXPECT_THAT(res.i1, Eq(NEW_VALUE));
|
||||
EXPECT_THAT(res.i2, Eq(OLD_VALUE));//we didn't serialized that
|
||||
}
|
||||
|
||||
TEST(SerializeExtensionPointer, PointerOwnerCanUseValueOverload) {
|
||||
auto *data = new int64_t{49845894};
|
||||
auto *res = new int64_t{-78548415};
|
||||
|
||||
PointerLinkingContext plctx1{};
|
||||
SerContext sctx1;
|
||||
sctx1.createSerializer(&plctx1).ext8b(data, PointerOwner{});
|
||||
sctx1.createDeserializer(&plctx1).ext8b(res, PointerOwner{});
|
||||
|
||||
EXPECT_THAT(*res, Eq(*data));
|
||||
|
||||
delete data;
|
||||
delete res;
|
||||
}
|
||||
|
||||
TEST(SerializeExtensionPointer, ReferencedByPointerCanUseValueOverload) {
|
||||
int64_t data{49845894};
|
||||
int64_t res{-78548415};
|
||||
|
||||
PointerLinkingContext plctx1{};
|
||||
SerContext sctx1;
|
||||
sctx1.createSerializer(&plctx1).ext8b(data, ReferencedByPointer{});
|
||||
sctx1.createDeserializer(&plctx1).ext8b(res, ReferencedByPointer{});
|
||||
|
||||
EXPECT_THAT(res, Eq(data));
|
||||
}
|
||||
|
||||
@@ -20,59 +20,73 @@
|
||||
//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::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 +96,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 +105,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(res, 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(res, 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 +176,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 +186,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 +200,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 +220,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 +235,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 +265,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(des, bitsery::ext::PolymorphicClassesList<BaseClone>{});
|
||||
des.ext(baseRes, PointerOwner{});
|
||||
EXPECT_THAT(sctx.br->error(), Eq(bitsery::ReaderError::InvalidPointer));
|
||||
}
|
||||
|
||||
@@ -20,14 +20,14 @@
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include "serialization_test_utils.h"
|
||||
|
||||
#include <bitsery/ext/std_map.h>
|
||||
#include <bitsery/ext/entropy.h>
|
||||
#include <unordered_map>
|
||||
#include <bitsery/traits/string.h>
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include "serialization_test_utils.h"
|
||||
|
||||
using StdMap = bitsery::ext::StdMap;
|
||||
|
||||
using testing::Eq;
|
||||
|
||||
@@ -20,9 +20,10 @@
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
#include <bitsery/ext/std_queue.h>
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include "serialization_test_utils.h"
|
||||
#include <bitsery/ext/std_queue.h>
|
||||
|
||||
using StdQueue = bitsery::ext::StdQueue;
|
||||
|
||||
|
||||
@@ -20,12 +20,12 @@
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include "serialization_test_utils.h"
|
||||
|
||||
#include <bitsery/ext/std_set.h>
|
||||
#include <set>
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include "serialization_test_utils.h"
|
||||
|
||||
using StdSet = bitsery::ext::StdSet;
|
||||
|
||||
using testing::Eq;
|
||||
|
||||
656
tests/serialization_ext_std_smart_ptr.cpp
Normal file
656
tests/serialization_ext_std_smart_ptr.cpp
Normal file
@@ -0,0 +1,656 @@
|
||||
//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/std_smart_ptr.h>
|
||||
#include <bitsery/ext/pointer.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::StdUniquePtr;
|
||||
using bitsery::ext::StdSharedPtr;
|
||||
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).registerBasesList(res, 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(res, 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 = StdUniquePtr;
|
||||
};
|
||||
|
||||
struct SharedPtrTest {
|
||||
template <typename T>
|
||||
using TData = std::shared_ptr<T>;
|
||||
using TExt = StdSharedPtr;
|
||||
};
|
||||
|
||||
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(res, 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(res, 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, StdSharedPtr{});
|
||||
ser.ext(baseData1, StdSharedPtr{});
|
||||
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, StdSharedPtr{});
|
||||
std::shared_ptr<Base> baseRes1{};
|
||||
auto& des = createDeserializer();
|
||||
des.ext(baseRes1, StdSharedPtr{});
|
||||
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, StdSharedPtr{});
|
||||
ser.ext(baseData2, StdSharedPtr{});
|
||||
ser.ext(baseData21, StdSharedPtr{});
|
||||
|
||||
std::shared_ptr<Base> baseRes1{};
|
||||
std::shared_ptr<Base> baseRes2{};
|
||||
std::shared_ptr<Base> baseRes21{};
|
||||
auto& des = createDeserializer();
|
||||
des.ext(baseRes1, StdSharedPtr{});
|
||||
des.ext(baseRes2, StdSharedPtr{});
|
||||
des.ext(baseRes21, StdSharedPtr{});
|
||||
|
||||
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, StdSharedPtr{});
|
||||
ser.ext(baseData11, StdSharedPtr{});
|
||||
ser.ext(baseData12, StdSharedPtr{});
|
||||
|
||||
std::shared_ptr<Base> baseRes1{};
|
||||
std::weak_ptr<Base> baseRes11{};
|
||||
std::weak_ptr<Base> baseRes12{};
|
||||
auto& des = createDeserializer();
|
||||
des.ext(baseRes1, StdSharedPtr{});
|
||||
des.ext(baseRes11, StdSharedPtr{});
|
||||
des.ext(baseRes12, StdSharedPtr{});
|
||||
|
||||
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, StdSharedPtr{});
|
||||
ser.ext(baseData11, StdSharedPtr{});
|
||||
ser.ext(baseData1, StdSharedPtr{});
|
||||
|
||||
std::shared_ptr<Base> baseRes1{};
|
||||
std::weak_ptr<Base> baseRes11{};
|
||||
std::weak_ptr<Base> baseRes2{};
|
||||
auto& des = createDeserializer();
|
||||
des.ext(baseRes2, StdSharedPtr{});
|
||||
des.ext(baseRes11, StdSharedPtr{});
|
||||
des.ext(baseRes1, StdSharedPtr{});
|
||||
|
||||
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, StdSharedPtr{});
|
||||
ser.ext(baseData2, StdSharedPtr{});
|
||||
ser.ext(baseData3, StdSharedPtr{});
|
||||
ser.ext(baseData11, StdSharedPtr{});
|
||||
|
||||
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, StdSharedPtr{});
|
||||
des.ext(baseRes2, StdSharedPtr{});
|
||||
des.ext(baseRes3, StdSharedPtr{});
|
||||
des.ext(baseRes11, StdSharedPtr{});
|
||||
|
||||
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, StdSharedPtr{});
|
||||
|
||||
std::shared_ptr<Base> baseRes1{new Derived{0, 0}};
|
||||
auto& des = createDeserializer();
|
||||
des.ext(baseRes1, StdSharedPtr{});
|
||||
|
||||
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, StdSharedPtr{});
|
||||
|
||||
std::shared_ptr<Base> baseRes1{new Base{}};
|
||||
auto& des = createDeserializer();
|
||||
des.ext(baseRes1, StdSharedPtr{});
|
||||
|
||||
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, StdSharedPtr{});
|
||||
|
||||
EXPECT_FALSE(isPointerContextValid());
|
||||
//
|
||||
// std::weak_ptr<Base> baseRes1{};
|
||||
// auto& des = createDeserializer();
|
||||
// des.ext(baseRes1, StdSharedPtr{});
|
||||
//
|
||||
// EXPECT_TRUE(isPointerContextValid());
|
||||
// clearSharedState();
|
||||
//
|
||||
// EXPECT_THAT(baseRes1.use_count(), Eq(1));
|
||||
// EXPECT_THAT(baseRes1->x, Eq(baseData1->x));
|
||||
// EXPECT_THAT(dynamic_cast<Derived*>(baseRes1.get()), ::testing::NotNull());
|
||||
|
||||
|
||||
}
|
||||
@@ -20,9 +20,10 @@
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
#include <bitsery/ext/std_stack.h>
|
||||
#include <gmock/gmock.h>
|
||||
#include "serialization_test_utils.h"
|
||||
#include <bitsery/ext/std_stack.h>
|
||||
|
||||
|
||||
using StdStack = bitsery::ext::StdStack;
|
||||
|
||||
|
||||
@@ -21,9 +21,10 @@
|
||||
//SOFTWARE.
|
||||
|
||||
|
||||
#include <bitsery/ext/value_range.h>
|
||||
#include <gmock/gmock.h>
|
||||
#include "serialization_test_utils.h"
|
||||
#include <bitsery/ext/value_range.h>
|
||||
|
||||
using namespace testing;
|
||||
using bitsery::details::RangeSpec;
|
||||
using bitsery::ext::BitsConstraint;
|
||||
|
||||
@@ -21,11 +21,12 @@
|
||||
//SOFTWARE.
|
||||
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include "serialization_test_utils.h"
|
||||
#include <bitsery/traits/string.h>
|
||||
#include <bitsery/traits/array.h>
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include "serialization_test_utils.h"
|
||||
|
||||
using testing::Eq;
|
||||
using testing::StrEq;
|
||||
using testing::ContainerEq;
|
||||
|
||||
@@ -21,9 +21,9 @@
|
||||
//SOFTWARE.
|
||||
|
||||
|
||||
#include <bitsery/traits/string.h>
|
||||
#include <gmock/gmock.h>
|
||||
#include "serialization_test_utils.h"
|
||||
#include <bitsery/traits/string.h>
|
||||
|
||||
using namespace testing;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user