polymorphism in progress

This commit is contained in:
Mindaugas Vinkelis
2018-04-12 09:20:25 +03:00
committed by Mindaugas
parent 1ca45aab79
commit 275c4138ee
40 changed files with 2145 additions and 610 deletions

View File

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

View File

@@ -41,13 +41,6 @@ install(DIRECTORY include/bitsery
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
#================ handle sub-projects =====================
# examples and tests are independend directories, that can be built separatelly.
# make find_package to no-op, when searching for "Bitsery", because it is created here.
macro(find_package)
if (NOT ${ARGV0} STREQUAL Bitsery)
_find_package(${ARGV})
endif()
endmacro()
if (BITSERY_BUILD_EXAMPLES)
message("build bitsery examples")

View File

@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2017 Mindaugas Vinkelis
Copyright (c) 2018 Mindaugas Vinkelis
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -70,6 +70,10 @@ using InputAdapter = InputBufferAdapter<Buffer>;
// s.template context<MyContext>();
using Context = std::tuple<int, std::pair<uint32_t, uint32_t>>;
//NOTE:
// if your context has no additional usage outside of serialization flow,
// then you can create it internally via configuration (see inheritance.cpp)
int main() {
MyTypes::GameState data{};

View File

@@ -11,7 +11,7 @@
//this header contains two extensions, that specifies inheritance type of base class
// BaseClass - normal inheritance
// VirtualBaseClass - when virtual inheritance is used
//in order for virtual inheritance to work, InheritanceContext is required.
//in order for virtual inheritance to work, InheritanceContext is required. for normal inheritance it is not required
//it can be created either internally (via configuration) or externally (pointer to context).
#include <bitsery/ext/inheritance.h>

View File

@@ -0,0 +1,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;
}

View File

@@ -37,8 +37,7 @@ namespace bitsery {
BufferIterators(TIterator begin, TIterator end)
: posIt{begin},
endIt{end}
{
endIt{end} {
}
friend details::SessionAccess;
@@ -54,18 +53,45 @@ namespace bitsery {
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)) {
}
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;
@@ -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{});
@@ -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)
@@ -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;

View File

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

View File

@@ -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;
using SwapTag = std::integral_constant<bool, Config::NetworkEndianness != details::getSystemEndianness()>;
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()>{});
void directReadValue(T& v, std::true_type) {
_inputAdapter.read(v);
v = details::swap(v);
}
template <typename T>
void _swapDataBits(T *v, size_t count, std::true_type) {
void directReadValue(T& v, std::false_type) {
_inputAdapter.read(v);
}
template<typename T>
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);
}
};

View File

@@ -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>>;
using SwapTag = std::integral_constant<bool, Config::NetworkEndianness != details::getSystemEndianness()>;
template <typename T>
void directWrite(T &&v, size_t count) {
_directWriteSwapTag(std::forward<T>(v), count, std::integral_constant<bool,
Config::NetworkEndianness != details::getSystemEndianness()>{});
void directWriteValue(const T& v, std::true_type) {
_outputAdapter.write(details::swap(v));
}
template <typename T>
void _directWriteSwapTag(const T *v, size_t count, std::true_type) {
void directWriteValue(const T& v, std::false_type) {
_outputAdapter.write(v);
}
template<typename T>
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));
}

View File

@@ -1,6 +1,24 @@
//MIT License
//
// Created by fraillt on 17.10.5.
//Copyright (c) 2018 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_DETAILS_SESSIONS_H
#define BITSERY_DETAILS_SESSIONS_H

View File

@@ -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 {
template<typename S>
PointerLinkingContext &getLinkingContext(S &s) {
auto res = s.template context<PointerLinkingContext>();
assert(res != nullptr);
return *res;
}
template<typename TObject>
struct RawPointerObjectHandler {
using TPointer = TObject;
namespace pointer_details {
template <typename T>
void create(TObject &obj) const {
obj = new T{};
struct PtrOwnerManager {
static_assert(std::is_pointer<T>::value, "");
using TElement = typename std::remove_pointer<T>::type;
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;
}
};
template <typename TObject>
struct RawPointerManagerConfig {
using RTTI = bitsery::ext::utils::StandardRTTI;
static constexpr PointerOwnershipType OwnershipType = PointerOwnershipType::Owner;
using Handler = RawPointerObjectHandler<TObject>;
static std::unique_ptr<utils::PointerSharedContextBase> createSharedContext(TObject &) {
return {};
static constexpr PointerOwnershipType getOwnership() {
return PointerOwnershipType::Observer;
}
static void restoreFromSharedContext(TObject &, utils::PointerSharedContextBase *) {
}
};
static void assign(T& obj, TElement* valuePtr) {
//do not delete existing object
obj = valuePtr;
}
class PointerOwner : public utils::PointerOwnerManager<details_pointer::RawPointerManagerConfig> {
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)
static void clear(T& obj) {
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 T>
struct NonPtrManager {
static_assert(!std::is_pointer<T>::value, "");
using TElement = T;
static TElement* getPtr(T& obj){
return &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);
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
};
}
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:
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;
};

View File

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

View File

@@ -0,0 +1,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

View File

@@ -67,9 +67,9 @@ namespace bitsery {
}
namespace traits {
template<typename T>
struct ExtensionTraits<ext::StdStack, T> {
using TValue = typename T::value_type;
template<typename T, typename Seq>
struct ExtensionTraits<ext::StdStack, std::stack<T, Seq>> {
using TValue = T;
static constexpr bool SupportValueOverload = true;
static constexpr bool SupportObjectOverload = true;
static constexpr bool SupportLambdaOverload = true;

View File

@@ -1,6 +1,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));
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 (!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());
}
}
} 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();

View File

@@ -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);
}
};
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;
};
}
namespace details_polymorphism {
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)));
};
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);
}
}
ObjectHandler _handler{};
};
template<typename S, typename TObject, template<typename> class TObjectManager>
class PolymorphicHandlersGenerator {
class PolymorphicHandlerBase {
public:
template<typename Fnc>
static void generate(Fnc &&addHandlerFnc) {
PolymorphicHandlersGenerator tmp{std::forward<Fnc>(addHandlerFnc)};
}
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:
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>();
void *fromBase(void *obj) const {
return RTTI::template cast<TBase, TDerived>(static_cast<TBase *>(obj));
}
template<typename TClass>
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;
}
};
struct BaseToDerivedKeyHashier {
size_t operator()(const BaseToDerivedKey &key) const {
return (key.baseHash + (key.baseHash << 6) + (key.derivedHash >> 2)) ^ key.derivedHash;
}
};
template<typename TSerializer, typename TBase, typename TDerived>
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;
addToMap<TSerializer, TBase, TDerived>(std::is_abstract<TDerived>{});
addChilds<TSerializer, TBase, TDerived>(typename PolymorphicBaseClass<TDerived>::Childs{});
}
template<typename TClassBase, typename T1, typename ... Tn>
void addChilds(details_polymorphism::List<T1, Tn...>) {
static_assert(std::is_base_of<TClassBase, T1>::value,
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<T1>();
addChilds<TClassBase>(details_polymorphism::List<Tn...>{});
add<TSerializer, TBase, T1>();
addChilds<TSerializer, TBase, TDerived>(PolymorphicClassesList<Tn...>{});
//iterate through derived class hierarchy as well
add<TSerializer, T1, T1>();
}
template<typename>
void addChilds(details_polymorphism::List<>) {
template<typename TSerializer, typename TBase, typename TDerived>
void addChilds(PolymorphicClassesList<>) {
}
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{};
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;
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;
}
);
void clear() {
_baseToDerivedMap.clear();
_baseToDerivedArray.clear();
}
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 {};
template<typename TSerializer, typename T1, typename ...Tn>
void registerBasesList(const TSerializer &s, PolymorphicClassesList<T1, Tn...>) {
add<TSerializer, T1, T1>();
registerBasesList<TSerializer>(s, PolymorphicClassesList<Tn...>{});
}
private:
std::unordered_map<size_t, std::pair<InheritanceTreeTypeId, std::unique_ptr<PolymorphicObjectHandlerInterface<Ser, TObject>>>> _map{};
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());
//convert derived hash to derived index, to make it work in cross-platform environment
auto &vec = _baseToDerivedArray.find(key.baseHash)->second;
auto derivedIndex = static_cast<size_t>(std::distance(vec.begin(), std::find(vec.begin(), vec.end(), key.derivedHash)));
details::writeSize(writer, derivedIndex);
//serialize
it->second->process(&ser, &obj);
};
template<typename Deserializer, typename Reader, typename TBase, typename TAssignFnc>
void deserialize(Deserializer &des, Reader &reader, TBase *obj, TAssignFnc assignFnc) {
size_t derivedIndex{};
details::readSize(reader, derivedIndex, std::numeric_limits<size_t>::max());
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;
}
);
}
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());
const PolymorphicObjectHandlerInterface<Des, TObject> *getHandler(const InheritanceTreeTypeId &id) {
auto it = _map.find(getHashFromId(id));
if (it != _map.end())
return it->second.get();
return nullptr;
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);
}
private:
size_t getHashFromId(const InheritanceTreeTypeId &id) const {
size_t res = id.depth << 16;
return res + id.index;
}
std::unordered_map<size_t, std::unique_ptr<PolymorphicObjectHandlerInterface<Des, TObject>>> _map{};
handler->process(&des, obj);
} else
reader.setError(ReaderError::InvalidPointer);
};
}
};
}
}
#endif //BITSERY_EXT_POLYMORPHISM_H
#endif //BITSERY_EXT_POLYMORPHISM_UTILS_H

View File

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

View File

@@ -6,7 +6,7 @@ set(CTEST_BINARY_DIRECTORY "build")
set(ENV{CXXFLAGS} "--coverage")
#when using Ninja generator, ctest_coverage cannot find files...
set(CTEST_CMAKE_GENERATOR "CodeBlocks - Unix Makefiles")
set(CTEST_USE_LAUNCHERS 1)
#set(CTEST_USE_LAUNCHERS 1)
set(CTEST_COVERAGE_COMMAND "gcov")

2
scripts/show_coverage.sh Normal file → Executable file
View File

@@ -1,5 +1,5 @@
#!/bin/sh
BUILD_DIR=$1
BUILD_DIR=./build
TESTS_BUILD_DIR=$BUILD_DIR/tests/CMakeFiles/
COV_INFO=$TESTS_BUILD_DIR/bitsery_coverage.info
lcov --directory $TESTS_BUILD_DIR --capture --output-file $COV_INFO

View File

@@ -20,13 +20,14 @@
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#include <gmock/gmock.h>
#include <bitsery/adapter/stream.h>
#include <bitsery/adapter_writer.h>
#include <bitsery/adapter_reader.h>
#include <bitsery/traits/vector.h>
#include <bitsery/traits/array.h>
#include <bitsery/traits/string.h>
#include <gmock/gmock.h>
#include <sstream>
//some helper types

View File

@@ -21,11 +21,11 @@
//SOFTWARE.
#include <gmock/gmock.h>
#include <bitsery/adapter_writer.h>
#include <bitsery/adapter_reader.h>
#include <bitsery/ext/value_range.h>
#include "serialization_test_utils.h"
#include <gmock/gmock.h>
using testing::Eq;
using testing::ContainerEq;

View File

@@ -21,9 +21,10 @@
//SOFTWARE.
#include <bitsery/ext/value_range.h>
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
#include <bitsery/ext/value_range.h>
using testing::Eq;
using testing::ContainerEq;

View File

@@ -20,9 +20,11 @@
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#include <bitsery/traits/string.h>
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
#include <bitsery/traits/string.h>
using testing::Eq;
using SessionsEnabledWriter = bitsery::AdapterWriter<OutputAdapter, SessionsEnabledConfig>;

View File

@@ -20,11 +20,12 @@
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
#include <bitsery/details/serialization_common.h>
#include <bitsery/traits/array.h>
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
using testing::Eq;
using testing::ContainerEq;
using bitsery::EndiannessType;

View File

@@ -21,10 +21,7 @@
//SOFTWARE.
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
#include <bitsery/flexible.h>
#include <bitsery/flexible/string.h>
#include <bitsery/flexible/array.h>
#include <bitsery/flexible/vector.h>
@@ -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) {

View File

@@ -22,16 +22,14 @@
#include <gmock/gmock.h>
#include <algorithm>
#include <numeric>
#include "serialization_test_utils.h"
#include <bitsery/traits/array.h>
#include <bitsery/traits/list.h>
#include <bitsery/traits/deque.h>
#include <bitsery/traits/forward_list.h>
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
using testing::ContainerEq;
using testing::Eq;

View File

@@ -21,11 +21,12 @@
//SOFTWARE.
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
#include <bitsery/ext/entropy.h>
#include <bitsery/traits/list.h>
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
using namespace testing;
using bitsery::ext::Entropy;

View File

@@ -20,9 +20,9 @@
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#include <bitsery/ext/growable.h>
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
#include <bitsery/ext/growable.h>
using namespace testing;

View File

@@ -20,9 +20,9 @@
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#include <bitsery/ext/inheritance.h>
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
#include <bitsery/ext/inheritance.h>
using bitsery::ext::BaseClass;
using bitsery::ext::VirtualBaseClass;

View File

@@ -20,9 +20,9 @@
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#include <bitsery/ext/pointer.h>
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
#include <bitsery/ext/pointer.h>
using bitsery::ext::PointerOwner;
using bitsery::ext::PointerObserver;
@@ -109,6 +109,7 @@ TEST_F(SerializeExtensionPointerSerialization, WhenPointersAreNullThenIsValid) {
EXPECT_THAT(plctx1.isValid(), Eq(true));
}
#ifndef NDEBUG
TEST(SerializeExtensionPointer, WhenPointerLinkingContextIsNullAndPointerIsNotNullThenAssert) {
@@ -379,6 +380,7 @@ struct Test1Data {
MyStruct1 *po1;
int32_t i1;
int32_t *pi1;
template<typename S>
void serialize(S &s) {
//set container elements to be candidates for non-owning pointers
@@ -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));
}

View File

@@ -20,29 +20,42 @@
//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;
};
@@ -73,6 +86,7 @@ void serialize(S& s, Derived2& o) {
struct MultipleVirtualInheritance : Derived1, Derived2 {
int8_t z{};
MultipleVirtualInheritance() = default;
MultipleVirtualInheritance(uint8_t x_, uint8_t y1_, uint8_t y2_, uint8_t z_) {
@@ -91,19 +105,40 @@ 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> {};
struct PolymorphicBaseClass<Base> : PolymorphicDerivedClasses<Derived1, Derived2> {
};
// 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<Derived1>: DerivedClasses<MultipleVirtualInheritance> {};
template <>
struct PolymorphicBaseClass<Derived2>: DerivedClasses<MultipleVirtualInheritance> {};
struct PolymorphicBaseClass<Derived2> : PolymorphicDerivedClasses<MultipleVirtualInheritance> {
};
}
}
@@ -112,19 +147,27 @@ namespace bitsery {
class SerializeExtensionPointerPolymorphicTypes : public testing::Test {
public:
TContext plctx1{};
SerContext sctx1{};
TContext plctx{};
SerContext sctx{};
typename SerContext::TSerializer &createSerializer() {
return sctx1.createSerializer(&plctx1);
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);
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 {
@@ -192,7 +235,7 @@ 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;
@@ -231,19 +274,57 @@ TEST_F(SerializeExtensionPointerPolymorphicTypes, WhenResultIsDifferentTypeThenR
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));
}

View File

@@ -20,14 +20,14 @@
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
#include <bitsery/ext/std_map.h>
#include <bitsery/ext/entropy.h>
#include <unordered_map>
#include <bitsery/traits/string.h>
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
using StdMap = bitsery::ext::StdMap;
using testing::Eq;

View File

@@ -20,9 +20,10 @@
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#include <bitsery/ext/std_queue.h>
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
#include <bitsery/ext/std_queue.h>
using StdQueue = bitsery::ext::StdQueue;

View File

@@ -20,12 +20,12 @@
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
#include <bitsery/ext/std_set.h>
#include <set>
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
using StdSet = bitsery::ext::StdSet;
using testing::Eq;

View File

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

View File

@@ -20,9 +20,10 @@
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#include <bitsery/ext/std_stack.h>
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
#include <bitsery/ext/std_stack.h>
using StdStack = bitsery::ext::StdStack;

View File

@@ -21,9 +21,10 @@
//SOFTWARE.
#include <bitsery/ext/value_range.h>
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
#include <bitsery/ext/value_range.h>
using namespace testing;
using bitsery::details::RangeSpec;
using bitsery::ext::BitsConstraint;

View File

@@ -21,11 +21,12 @@
//SOFTWARE.
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
#include <bitsery/traits/string.h>
#include <bitsery/traits/array.h>
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
using testing::Eq;
using testing::StrEq;
using testing::ContainerEq;

View File

@@ -21,9 +21,9 @@
//SOFTWARE.
#include <bitsery/traits/string.h>
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
#include <bitsery/traits/string.h>
using namespace testing;