added custom allocator support for pointer like objects.

This commit is contained in:
Mindaugas Vinkelis
2019-03-13 11:06:55 +02:00
committed by Mindaugas Vinkelis
parent 03f2c3c8b5
commit 57dd028b7a
14 changed files with 1047 additions and 287 deletions

View File

@@ -1,3 +1,6 @@
### Improvements
added support for custom allocator(s) for pointer like objects. More information on how to correctly use custom allocation and pointers in general see [this](doc/design/pointers.md).
# [4.6.1](https://github.com/fraillt/bitsery/compare/v4.6.0...v4.6.1) (2019-06-27)
### Features

View File

@@ -9,7 +9,7 @@ Library design:
* `extending library functionality`
* `errors handling`
* `forward/backward compatibility via Growable extension`
* `pointers`
* [pointers](design/pointers.md)
* `inheritance`
* `polymorphism`

40
doc/design/pointers.md Normal file
View File

@@ -0,0 +1,40 @@
*document in progress*
## Extensions
Raw pointers are managed by three extensions:
* **PointerOwner** - manages a lifetime of the pointer, creates or destroys if required.
* **PointerObserver** - doesn't own pointer so it doesn't create or destroy anything.
* **ReferencedByPointer** - when a non-owning pointer (*PointerObserver*) points to reference type, this extension marks this object as a valid target for PointerObserver.
"Smart" pointers, from c++ standard lib (std), are managed by:
* **StdSmartPtr** - can accept unique_ptr, shared_ptr and weak_ptr
## Implementation details
All aforementioned extensions derive from single base class `PointerObjectExtensionBase`.
This base class accepts three template parameters that customise its behaviour.
* `TPtrManager\<T\>` - describes how particular pointer type should be handled:
pointer creation/destruction describes a type of pointer (e.g. owning, shared, observer), and how to actually get value for pointer object.
This is the place to start if you want to implement pointer support for your custom type. \<T\> is type of pointer object, e.g. `std::unique_ptr<MyType>`, `MyType*`
* `TPolymorphicContext\<RTTI\>` - provides the functionality to register class hierarchies for your types with serializer, and deserializer, in order to polymorphically serialize/deserialize objects.
\<RTTI\> template parameter provides runtime information about a type that is used to construct class hierarchies and save them to read/write them to buffer.
* `RTTI` - this template parameter provides information if a type is polymorphic, and if it is, then it is used in `TPolymorphicContext\<RTTI\>`.
Some pointer managers, like `PointerObserver` and `ReferencedByPointer` never requires polymorphic context. In these cases, you need to provide RTTI that will return `isPolymorphic`=false for all types.
By default all pointers extensions use `StandardRTTI` from "/ext/utils/rtti_utils.h" that internally uses `typeid` and `dynamic_cast`.
If your environment doesn't allow RTTI, you can provide your own RTTI for your types.
## Allocators
Allocation is implemented using dynamic type for allocator (similar to `std::pmr::memory_resource` from c++17),
and it is called `MemResourceBase` from "ext/utils/memory_allocator.h". The core difference between standard one and this allocator
is that it has additional `size_t typeId` field for `allocate` and `deallocate` methods, this value is returned from `RTTI`.
There are few options to customise pointer allocation for your pointers:
* provide default allocator in `PointerLinkingContext` by calling `setMemResource`.
* provide an instance of `MemResourceBase*` to pointer manager constructor, along with boolean parameter that specifies if this memory resource should propagate when deserializing child objects.
If no memory resource is provided, then `MemResourceNewDelete` is used, which calls `::operator new(bytes)` and `::operator delete(ptr)`.
**IMPORTANT**: there are few things that you should know to correctly use custom allocations with `StdSmartPtr`:
* Memory resource must live as long as the last object, that was allocated with it (this is required by std::shared_ptr, custom deleter is provided, that will be able to deallocate correctly when a shared pointer is destroyed).
* std::unique_ptr acts differently with resources that it owns, depending on what deleter is used.
* if default deleter is used, then a pointer is *released* and deallocated using provided memory resource.
* if custom deleter is used, then a pointer is deallocated using this custom deleter.

View File

@@ -47,8 +47,8 @@ namespace bitsery {
return T{};
}
template <typename T>
static T* createInHeap() {
return new T{};
static T* create(void* ptr) {
return new(ptr) T{};
}
};

View File

@@ -41,7 +41,7 @@ namespace bitsery {
using TElement = typename std::remove_pointer<T>::type;
static TElement* getPtr(T &obj) {
static TElement* getPtr(T& obj) {
return obj;
}
@@ -49,15 +49,26 @@ namespace bitsery {
return PointerOwnershipType::Owner;
}
static void assign(T& obj, TElement* valuePtr) {
delete obj;
obj = valuePtr;
static void create(T& obj, PolymorphicAllocator& alloc, size_t typeId) {
obj = alloc.allocate<TElement>(typeId);
}
static void clear(T& obj) {
delete obj;
static void createPolymorphic(T& obj, PolymorphicAllocator& alloc,
const std::shared_ptr<PolymorphicHandlerBase>& handler) {
obj = static_cast<TElement*>(handler->create(alloc));
}
static void destroy(T& obj, PolymorphicAllocator& alloc, size_t typeId) {
alloc.deallocate(obj, typeId);
obj = nullptr;
}
static void destroyPolymorphic(T& obj, PolymorphicAllocator& alloc,
const std::shared_ptr<PolymorphicHandlerBase>& handler) {
handler->destroy(alloc, obj);
obj = nullptr;
}
};
template<typename T>
@@ -66,11 +77,6 @@ namespace bitsery {
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;
}
static TElement* getPtr(T& obj) {
return obj;
}
@@ -79,12 +85,17 @@ namespace bitsery {
return PointerOwnershipType::Observer;
}
static void assign(T& obj, TElement* valuePtr) {
//do not delete existing object
obj = valuePtr;
//pure observer doesn't have create/createPolymorphic methods, but instead returns reference to pointer
//which gets updated later
static TElement*& getPtrRef(T& obj) {
return obj;
}
static void clear(T& obj) {
static void destroy(T& obj, PolymorphicAllocator& alloc, size_t typeId) {
obj = nullptr;
}
static void destroyPolymorphic(T& obj, PolymorphicAllocator& alloc, PolymorphicHandlerBase& handler) {
obj = nullptr;
}
@@ -107,9 +118,22 @@ namespace bitsery {
// this code is unreachable for reference type, but is necessary to compile
// LCOV_EXCL_START
static void assign(T& , TElement* ) {}
static void clear(T& ) {}
static void create(T& obj, PolymorphicAllocator& alloc, size_t typeId) {
}
static void createPolymorphic(T& obj, PolymorphicAllocator& alloc, PolymorphicHandlerBase& handler) {
}
static void destroy(T& obj, PolymorphicAllocator& alloc, size_t typeId) {
}
static void destroyPolymorphic(T& obj, PolymorphicAllocator& alloc, PolymorphicHandlerBase& handler) {
}
// LCOV_EXCL_STOP
};
@@ -117,7 +141,7 @@ namespace bitsery {
// this class is used by NonPtrManager
struct NoRTTI {
template<typename TBase>
static size_t get(TBase& ) {
static size_t get(TBase&) {
return 0;
}
@@ -142,20 +166,20 @@ namespace bitsery {
template<typename RTTI>
using PointerOwnerBase = pointer_utils::PointerObjectExtensionBase<
pointer_details::PtrOwnerManager, PolymorphicContext, RTTI>;
pointer_details::PtrOwnerManager, PolymorphicContext, RTTI>;
using PointerOwner = PointerOwnerBase<StandardRTTI>;
using PointerObserver = pointer_utils::PointerObjectExtensionBase<
pointer_details::PtrObserverManager, PolymorphicContext, pointer_details::NoRTTI>;
pointer_details::PtrObserverManager, PolymorphicContext, pointer_details::NoRTTI>;
//inherit from PointerObjectExtensionBase in order to specify PointerType::NotNull
class ReferencedByPointer : public pointer_utils::PointerObjectExtensionBase<
pointer_details::NonPtrManager, PolymorphicContext, pointer_details::NoRTTI> {
pointer_details::NonPtrManager, PolymorphicContext, pointer_details::NoRTTI> {
public:
ReferencedByPointer() : pointer_utils::PointerObjectExtensionBase<
pointer_details::NonPtrManager, PolymorphicContext, pointer_details::NoRTTI>(
PointerType::NotNull) {}
pointer_details::NonPtrManager, PolymorphicContext, pointer_details::NoRTTI>(
PointerType::NotNull) {}
};
}

View File

@@ -46,16 +46,16 @@ namespace bitsery {
using TElement = typename T::element_type;
template <typename TDeleter>
static TElement *getPtr(std::unique_ptr<TElement, TDeleter> &obj) {
template<typename TDeleter>
static TElement* getPtr(std::unique_ptr<TElement, TDeleter>& obj) {
return obj.get();
}
static TElement *getPtr(std::shared_ptr<TElement> &obj) {
static TElement* getPtr(std::shared_ptr<TElement>& obj) {
return obj.get();
}
static TElement *getPtr(std::weak_ptr<TElement> &obj) {
static TElement* getPtr(std::weak_ptr<TElement>& obj) {
if (auto ptr = obj.lock())
return ptr.get();
return nullptr;
@@ -69,34 +69,140 @@ namespace bitsery {
: PointerOwnershipType::SharedObserver;
}
static void clear(T &obj) {
template<typename TDeleter>
static void create(std::unique_ptr<TElement, TDeleter>& obj, PolymorphicAllocator& alloc,
size_t typeId) {
obj.reset(alloc.allocate<TElement>(typeId));
}
template<typename TDeleter>
static void createPolymorphic(std::unique_ptr<TElement, TDeleter>& obj, PolymorphicAllocator& alloc,
const std::shared_ptr<PolymorphicHandlerBase>& handler) {
obj.reset(static_cast<TElement*>(handler->create(alloc)));
}
template<typename TDel>
static void destroy(std::unique_ptr<TElement, TDel>& obj, PolymorphicAllocator& alloc, size_t typeId) {
uniquePtrDestroy(obj, alloc, typeId, std::is_same<std::unique_ptr<TElement>, T>{});
}
template<typename TDel>
static void destroyPolymorphic(std::unique_ptr<TElement, TDel>& obj, PolymorphicAllocator& alloc,
const std::shared_ptr<PolymorphicHandlerBase>& handler) {
uniquePtrDestroyPolymorphic(obj, alloc, handler, std::is_same<std::unique_ptr<TElement>, T>{});
}
static void destroy(std::shared_ptr<TElement>& obj, PolymorphicAllocator&, size_t) {
obj.reset();
}
static void assign(T &obj, TElement *valuePtr) {
obj.reset(valuePtr);
static void destroyPolymorphic(std::shared_ptr<TElement>& obj, PolymorphicAllocator&,
const std::shared_ptr<PolymorphicHandlerBase>&) {
obj.reset();
}
//this is used, when old object exists and is the same type
static std::unique_ptr<pointer_utils::PointerSharedStateBase> saveToSharedState(T &obj) {
static void destroy(std::weak_ptr<TElement>& obj, PolymorphicAllocator&, size_t) {
obj.reset();
}
static void destroyPolymorphic(std::weak_ptr<TElement>& obj, PolymorphicAllocator&,
const std::shared_ptr<PolymorphicHandlerBase>&) {
obj.reset();
}
static std::unique_ptr<pointer_utils::PointerSharedStateBase> createShared(
std::shared_ptr<TElement>& obj, PolymorphicAllocator& alloc, size_t typeId) {
// capture deleter parameters by value
obj = std::shared_ptr<TElement>(alloc.allocate<TElement>(typeId),
[&alloc, typeId](TElement* data) {
alloc.deallocate(data, typeId);
});
auto state = new SharedPtrSharedState{};
state->obj = obj;
return std::unique_ptr<pointer_utils::PointerSharedStateBase>{state};
}
static std::unique_ptr<pointer_utils::PointerSharedStateBase> createSharedPolymorphic(
std::shared_ptr<TElement>& obj, PolymorphicAllocator& alloc,
const std::shared_ptr<PolymorphicHandlerBase>& handler) {
// capture deleter parameters by value
obj = std::shared_ptr<TElement>(static_cast<TElement*>(handler->create(alloc)),
[alloc, handler](TElement* data) {
handler->destroy(alloc, data);
});
auto state = new SharedPtrSharedState{};
state->obj = obj;
return std::unique_ptr<pointer_utils::PointerSharedStateBase>{state};
}
static std::unique_ptr<pointer_utils::PointerSharedStateBase> createShared(
std::weak_ptr<TElement>& obj, PolymorphicAllocator& alloc, size_t typeId) {
auto res = std::shared_ptr<TElement>(alloc.allocate<TElement>(typeId),
[alloc, typeId](TElement* data) {
alloc.deallocate(data, typeId);
});
obj = res;
auto state = new SharedPtrSharedState{};
state->obj = res;
return std::unique_ptr<pointer_utils::PointerSharedStateBase>{state};
}
static std::unique_ptr<pointer_utils::PointerSharedStateBase> createSharedPolymorphic(
std::weak_ptr<TElement>& obj, PolymorphicAllocator& alloc,
const std::shared_ptr<PolymorphicHandlerBase>& handler) {
auto res = std::shared_ptr<TElement>(static_cast<TElement*>(handler->create(alloc)),
[alloc, handler](TElement* data) {
handler->destroy(alloc, data);
});
obj = res;
auto state = new SharedPtrSharedState{};
state->obj = res;
return std::unique_ptr<pointer_utils::PointerSharedStateBase>{state};
}
static std::unique_ptr<pointer_utils::PointerSharedStateBase> getSharedState(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);
}
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);
private:
template<typename TDel>
static void
uniquePtrDestroy(std::unique_ptr<TElement, TDel>& obj, PolymorphicAllocator& alloc, size_t typeId,
std::true_type) {
auto ptr = obj.release();
alloc.deallocate(ptr, typeId);
}
template<typename TDel>
static void
uniquePtrDestroyPolymorphic(std::unique_ptr<TElement, TDel>& obj, PolymorphicAllocator& alloc,
const std::shared_ptr<PolymorphicHandlerBase>& handler, std::true_type) {
auto ptr = obj.release();
handler->destroy(alloc, ptr);
}
template<typename TDel>
static void
uniquePtrDestroy(std::unique_ptr<TElement, TDel>& obj, PolymorphicAllocator&, size_t,
std::false_type) {
obj.reset();
}
template<typename TDel>
static void
uniquePtrDestroyPolymorphic(std::unique_ptr<TElement, TDel>& obj, PolymorphicAllocator&,
const std::shared_ptr<PolymorphicHandlerBase>&, std::false_type) {
obj.reset();
}
};
@@ -104,7 +210,7 @@ namespace bitsery {
template<typename RTTI>
using StdSmartPtrBase = pointer_utils::PointerObjectExtensionBase<
smart_ptr_details::SmartPtrOwnerManager, PolymorphicContext, RTTI>;
smart_ptr_details::SmartPtrOwnerManager, PolymorphicContext, RTTI>;
//helper type for convienience
using StdSmartPtr = StdSmartPtrBase<StandardRTTI>;

View File

@@ -0,0 +1,95 @@
//MIT License
//
//Copyright (c) 2018 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_EXT_MEMORY_ALLOCATOR_H
#define BITSERY_EXT_MEMORY_ALLOCATOR_H
#include "../../details/serialization_common.h"
#include <new>
namespace bitsery {
namespace ext {
// these are very similar to c++17 polymorphic allocator and memory resource classes
// but i don't want to enforce users to use c++17 if they want to use pointers
// plus this has additional information from RTTI about runtime type information,
// might be useful working with polymorphic types
class MemResourceBase {
public:
virtual void* allocate(size_t bytes, size_t alignment, size_t typeId) = 0;
virtual void deallocate(void* ptr, size_t bytes, size_t alignment, size_t typeId) = 0;
virtual ~MemResourceBase() noexcept = default;
};
class MemResourceNewDelete : public MemResourceBase {
public:
inline void* allocate(size_t bytes, size_t /*alignment*/, size_t /*typeId*/) final {
return (::operator new(bytes));
}
inline void deallocate(void* ptr, size_t /*bytes*/, size_t /*alignment*/, size_t /*typeId*/) final {
(::operator delete(ptr));
}
~MemResourceNewDelete() noexcept final = default;
};
class PolymorphicAllocator {
public:
template<typename T>
T* allocate(size_t typeId) const {
constexpr auto bytes = sizeof(T);
constexpr auto alignment = std::alignment_of<T>::value;
void* ptr = _resource
? _resource->allocate(bytes, alignment, typeId)
: MemResourceNewDelete{}.allocate(bytes, alignment, typeId);
return ::bitsery::Access::create<T>(ptr);
}
template<typename T>
void deallocate(T* ptr, size_t typeId) const {
constexpr auto bytes = sizeof(T);
constexpr auto alignment = std::alignment_of<T>::value;
ptr->~T();
_resource
? _resource->deallocate(ptr, bytes, alignment, typeId)
: MemResourceNewDelete{}.deallocate(ptr, bytes, alignment, typeId);
}
void setMemResource(MemResourceBase* resource) {
_resource = resource;
}
MemResourceBase* getMemResource() const {
return _resource;
}
private:
MemResourceBase* _resource{nullptr};
};
}
}
#endif //BITSERY_EXT_MEMORY_ALLOCATOR_H

View File

@@ -28,6 +28,7 @@
#include <memory>
#include <algorithm>
#include <cassert>
#include "polymorphism_utils.h"
#include "../../details/adapter_utils.h"
#include "../../details/serialization_common.h"
@@ -35,7 +36,7 @@ namespace bitsery {
namespace ext {
//change name
enum class PointerType {
enum class PointerType : uint8_t {
Nullable,
NotNull
};
@@ -54,9 +55,6 @@ namespace bitsery {
SharedObserver
};
//forward declaration
class PointerLinkingContext;
namespace pointer_utils {
//this class is used to store context for shared ptr owners
@@ -67,8 +65,8 @@ namespace bitsery {
//PLC info is internal classes for serializer, and deserializer
struct PLCInfo {
explicit PLCInfo(PointerOwnershipType ownershipType_)
: ownershipType{ownershipType_},
isSharedProcessed{false} {};
: ownershipType{ownershipType_},
isSharedProcessed{false} {};
PointerOwnershipType ownershipType;
bool isSharedProcessed;
@@ -82,7 +80,8 @@ namespace bitsery {
return;
}
//only shared ownership can get here multiple times
assert(ptrType == PointerOwnershipType::SharedOwner || ptrType == PointerOwnershipType::SharedObserver);
assert(ptrType == PointerOwnershipType::SharedOwner ||
ptrType == PointerOwnershipType::SharedObserver);
//check if need to update to SharedOwner
if (ptrType == PointerOwnershipType::SharedOwner)
ownershipType = ptrType;
@@ -91,33 +90,37 @@ namespace bitsery {
}
};
struct PLCInfoSerializer: PLCInfo {
struct PLCInfoSerializer : PLCInfo {
PLCInfoSerializer(size_t id_, PointerOwnershipType ownershipType_)
: PLCInfo(ownershipType_), id{id_} {}
: PLCInfo(ownershipType_), id{id_} {}
size_t id;
};
struct PLCInfoDeserializer : PLCInfo {
PLCInfoDeserializer(void *ptr, PointerOwnershipType ownershipType_)
: PLCInfo(ownershipType_),
ownerPtr{ptr} {};
PLCInfoDeserializer(void* ptr, PointerOwnershipType ownershipType_)
: PLCInfo(ownershipType_),
ownerPtr{ptr} {};
//need to override these explicitly because we have pointer member
PLCInfoDeserializer(const PLCInfoDeserializer&) = delete;
PLCInfoDeserializer(PLCInfoDeserializer&&) = default;
PLCInfoDeserializer& operator =(const PLCInfoDeserializer&) = delete;
PLCInfoDeserializer& operator =(PLCInfoDeserializer&&) = default;
void processOwner(void *ptr) {
PLCInfoDeserializer(PLCInfoDeserializer&&) = default;
PLCInfoDeserializer& operator=(const PLCInfoDeserializer&) = delete;
PLCInfoDeserializer& operator=(PLCInfoDeserializer&&) = default;
void processOwner(void* ptr) {
ownerPtr = ptr;
assert(ownershipType != PointerOwnershipType::Observer);
for (auto &o:observersList)
for (auto& o:observersList)
o.get() = ptr;
observersList.clear();
observersList.shrink_to_fit();
}
void processObserver(void *(&ptr)) {
void processObserver(void* (& ptr)) {
if (ownerPtr) {
ptr = ownerPtr;
} else {
@@ -125,30 +128,30 @@ namespace bitsery {
}
}
void *ownerPtr;
std::vector<std::reference_wrapper<void *>> observersList{};
void* ownerPtr;
std::vector<std::reference_wrapper<void*>> observersList{};
std::unique_ptr<PointerSharedStateBase> sharedState{};
};
class PointerLinkingContextSerialization {
public:
explicit PointerLinkingContextSerialization()
: _currId{0},
_ptrMap{} {}
: _currId{0},
_ptrMap{} {}
PointerLinkingContextSerialization(const PointerLinkingContextSerialization &) = delete;
PointerLinkingContextSerialization(const PointerLinkingContextSerialization&) = delete;
PointerLinkingContextSerialization &operator=(const PointerLinkingContextSerialization &) = delete;
PointerLinkingContextSerialization& operator=(const PointerLinkingContextSerialization&) = delete;
PointerLinkingContextSerialization(PointerLinkingContextSerialization &&) = default;
PointerLinkingContextSerialization(PointerLinkingContextSerialization&&) = default;
PointerLinkingContextSerialization &operator=(PointerLinkingContextSerialization &&) = default;
PointerLinkingContextSerialization& operator=(PointerLinkingContextSerialization&&) = default;
~PointerLinkingContextSerialization() = default;
const PLCInfoSerializer &getInfoByPtr(const void *ptr, PointerOwnershipType ptrType) {
const PLCInfoSerializer& getInfoByPtr(const void* ptr, PointerOwnershipType ptrType) {
auto res = _ptrMap.emplace(ptr, PLCInfoSerializer{_currId + 1u, ptrType});
auto &ptrInfo = res.first->second;
auto& ptrInfo = res.first->second;
if (res.second) {
++_currId;
return ptrInfo;
@@ -161,7 +164,7 @@ namespace bitsery {
//we cannot serialize pointers, if we haven't serialized objects themselves
bool isPointerSerializationValid() const {
return std::all_of(_ptrMap.begin(), _ptrMap.end(),
[](const std::pair<const void *, PLCInfoSerializer> &p) {
[](const std::pair<const void*, PLCInfoSerializer>& p) {
return p.second.ownershipType == PointerOwnershipType::SharedOwner ||
p.second.ownershipType == PointerOwnershipType::Owner;
});
@@ -169,42 +172,42 @@ namespace bitsery {
private:
size_t _currId;
std::unordered_map<const void *, PLCInfoSerializer> _ptrMap;
std::unordered_map<const void*, PLCInfoSerializer> _ptrMap;
};
class PointerLinkingContextDeserialization {
public:
explicit PointerLinkingContextDeserialization()
: _idMap{} {}
: _idMap{} {}
PointerLinkingContextDeserialization(const PointerLinkingContextDeserialization &) = delete;
PointerLinkingContextDeserialization(const PointerLinkingContextDeserialization&) = delete;
PointerLinkingContextDeserialization &operator=(const PointerLinkingContextDeserialization &) = delete;
PointerLinkingContextDeserialization& operator=(const PointerLinkingContextDeserialization&) = delete;
PointerLinkingContextDeserialization(PointerLinkingContextDeserialization &&) = default;
PointerLinkingContextDeserialization(PointerLinkingContextDeserialization&&) = default;
PointerLinkingContextDeserialization &operator=(PointerLinkingContextDeserialization &&) = default;
PointerLinkingContextDeserialization& operator=(PointerLinkingContextDeserialization&&) = default;
~PointerLinkingContextDeserialization() = default;
PLCInfoDeserializer &getInfoById(size_t id, PointerOwnershipType ptrType) {
PLCInfoDeserializer& getInfoById(size_t id, PointerOwnershipType ptrType) {
auto res = _idMap.emplace(id, PLCInfoDeserializer{nullptr, ptrType});
auto &ptrInfo = res.first->second;
auto& ptrInfo = res.first->second;
if (!res.second)
ptrInfo.update(ptrType);
return ptrInfo;
}
void clearSharedState() {
for (auto &item: _idMap)
for (auto& item: _idMap)
item.second.sharedState.reset();
}
//valid, when all pointers has owners
bool isPointerDeserializationValid() const {
return std::all_of(_idMap.begin(), _idMap.end(),
[](const std::pair<const size_t, PLCInfoDeserializer> &p) {
[](const std::pair<const size_t, PLCInfoDeserializer>& p) {
return p.second.ownershipType == PointerOwnershipType::SharedOwner ||
p.second.ownershipType == PointerOwnershipType::Owner;
});
@@ -213,23 +216,54 @@ namespace bitsery {
private:
std::unordered_map<size_t, PLCInfoDeserializer> _idMap;
};
}
//this class is for convenience
class PointerLinkingContext :
public pointer_utils::PointerLinkingContextSerialization,
public pointer_utils::PointerLinkingContextDeserialization,
public PolymorphicAllocator {
public:
explicit PointerLinkingContext() = default;
bool isValid() {
return isPointerSerializationValid() && isPointerDeserializationValid();
}
};
namespace pointer_utils {
template<template<typename> class TPtrManager,
template<typename> class TPolymorphicContext, typename RTTI>
template<typename> class TPolymorphicContext, typename RTTI>
class PointerObjectExtensionBase {
public:
explicit PointerObjectExtensionBase(PointerType ptrType = PointerType::Nullable) :
_ptrType{ptrType} {}
// helper types
template<typename T>
struct IsPolymorphic : std::integral_constant<bool,
RTTI::template isPolymorphic<typename TPtrManager<T>::TElement>()> {
};
template<PointerOwnershipType Value>
using OwnershipType = std::integral_constant<PointerOwnershipType, Value>;
explicit PointerObjectExtensionBase(PointerType ptrType = PointerType::Nullable,
MemResourceBase* resource = nullptr,
bool resourcePropagate = false) :
_ptrType{ptrType},
_resourcePropagate{resourcePropagate},
_resource{resource} {
}
template<typename Ser, typename Writer, typename T, typename Fnc>
void serialize(Ser &ser, Writer &w, const T &obj, Fnc &&fnc) const {
void serialize(Ser& ser, Writer& w, const T& obj, Fnc&& fnc) const {
auto ptr = TPtrManager<T>::getPtr(const_cast<T &>(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(getBasePtr(ptr), TPtrManager<T>::getOwnership());
auto& ptrInfo = ctx->getInfoByPtr(getBasePtr(ptr), TPtrManager<T>::getOwnership());
details::writeSize(w, ptrInfo.id);
if (TPtrManager<T>::getOwnership() != PointerOwnershipType::Observer) {
if (!ptrInfo.isSharedProcessed)
@@ -243,18 +277,23 @@ namespace bitsery {
}
template<typename Des, typename Reader, typename T, typename Fnc>
void deserialize(Des &des, Reader &r, T &obj, Fnc &&fnc) const {
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 ctx = des.template context<PointerLinkingContext>();
assert(ctx != nullptr);
if (id) {
auto ctx = des.template context<PointerLinkingContext>();
assert(ctx != nullptr);
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()>{});
auto& ptrInfo = ctx->getInfoById(id, TPtrManager<T>::getOwnership());
deserializeImpl(*ctx, ptrInfo, des, obj, std::forward<Fnc>(fnc), r, IsPolymorphic<T>{},
OwnershipType<TPtrManager<T>::getOwnership()>{});
} else {
if (_ptrType == PointerType::Nullable) {
TPtrManager<T>::clear(obj);
if (auto ptr = TPtrManager<T>::getPtr(obj)) {
auto prevMemResource = ctx->getMemResource();
if (_resource) ctx->setMemResource(_resource);
destroyPtr(*ctx, des, obj, IsPolymorphic<T>{});
ctx->setMemResource(prevMemResource);
};
} else
r.setError(ReaderError::InvalidPointer);
}
@@ -262,13 +301,23 @@ namespace bitsery {
private:
template<typename T>
struct IsPolymorphic : std::integral_constant<bool,
RTTI::template isPolymorphic<typename TPtrManager<T>::TElement>()> {
};
template<typename Des, typename TObj>
void destroyPtr(PointerLinkingContext& plc, Des& des, TObj& obj,
std::true_type /*polymorphic*/) const {
const auto& ctx = des.template context<TPolymorphicContext<RTTI>>();
auto ptr = TPtrManager<TObj>::getPtr(obj);
TPtrManager<TObj>::destroyPolymorphic(obj, plc, ctx->getPolymorphicHandler(*ptr));
}
template<typename Des, typename TObj>
void destroyPtr(PointerLinkingContext& plc, Des&, TObj& obj,
std::false_type /*polymorphic*/) const {
TPtrManager<TObj>::destroy(obj, plc, RTTI::template get<typename TPtrManager<TObj>::TElement>());
}
template<typename T>
const void *getBasePtr(const T *ptr) const {
const void* getBasePtr(const T* ptr) const {
// todo implement handling of types with virtual inheritance
// this is required to correctly track same object, when one object is derived and other is base class
// e.g. shared_ptr<Base> and weak_ptr<Derived> or pointer observer Base*
@@ -276,111 +325,128 @@ namespace bitsery {
}
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>>();
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 {
void serializeImpl(Ser&, TPtr& ptr, Fnc&& fnc, Writer&, std::false_type) const {
fnc(*ptr);
}
template<typename Des, typename T, typename Fnc, typename Reader>
void deserializeImpl(PLCInfoDeserializer &ptrInfo, Des &des, T &obj, Fnc &&,
Reader &r, std::true_type ,
std::integral_constant<PointerOwnershipType, PointerOwnershipType::Owner>) const {
const auto &ctx = des.template context<TPolymorphicContext<RTTI>>();
void deserializeImpl(PointerLinkingContext& plc, PLCInfoDeserializer& ptrInfo, Des& des, T& obj, Fnc&&,
Reader& r, std::true_type, OwnershipType<PointerOwnershipType::Owner>) const {
const auto& ctx = des.template context<TPolymorphicContext<RTTI>>();
auto prevMemResource = plc.getMemResource();
ctx->deserialize(des, r, TPtrManager<T>::getPtr(obj),
[&obj, this](typename TPtrManager<T>::TElement *valuePtr) {
TPtrManager<T>::assign(obj, valuePtr);
[&obj, &plc, this, prevMemResource](
const std::shared_ptr<PolymorphicHandlerBase>& handler) {
if (_resource) plc.setMemResource(_resource);
TPtrManager<T>::createPolymorphic(obj, plc, handler);
if (!_resourcePropagate) plc.setMemResource(prevMemResource);
return TPtrManager<T>::getPtr(obj);
},
[&obj, &plc, this](const std::shared_ptr<PolymorphicHandlerBase>& handler) {
if (_resource) plc.setMemResource(_resource);
TPtrManager<T>::destroyPolymorphic(obj, plc, handler);
});
plc.setMemResource(prevMemResource);
ptrInfo.processOwner(TPtrManager<T>::getPtr(obj));
}
template<typename Des, typename T, typename Fnc, typename Reader>
void deserializeImpl(PLCInfoDeserializer &ptrInfo, Des &, T &obj, Fnc &&fnc,
Reader &, std::false_type ,
std::integral_constant<PointerOwnershipType, PointerOwnershipType::Owner>) const {
void deserializeImpl(PointerLinkingContext& plc, PLCInfoDeserializer& ptrInfo, Des&, T& obj, Fnc&& fnc,
Reader&, std::false_type, OwnershipType<PointerOwnershipType::Owner>) const {
auto ptr = TPtrManager<T>::getPtr(obj);
if (ptr) {
fnc(*ptr);
} else {
ptr = ::bitsery::Access::createInHeap<typename TPtrManager<T>::TElement>();
auto prevMemResource = plc.getMemResource();
if (_resource) plc.setMemResource(_resource);
TPtrManager<T>::create(obj, plc, RTTI::template get<typename TPtrManager<T>::TElement>());
if (!_resourcePropagate) plc.setMemResource(prevMemResource);
ptr = TPtrManager<T>::getPtr(obj);
fnc(*ptr);
TPtrManager<T>::assign(obj, ptr);
plc.setMemResource(prevMemResource);
}
ptrInfo.processOwner(ptr);
}
template<typename Des, typename T, typename Fnc, typename Reader>
void deserializeImpl(PLCInfoDeserializer &ptrInfo, Des &des, T &obj, Fnc &&,
Reader &r, std::true_type ,
std::integral_constant<PointerOwnershipType, PointerOwnershipType::SharedOwner>) const {
auto &sharedState = ptrInfo.sharedState;
void deserializeImpl(PointerLinkingContext& plc, PLCInfoDeserializer& ptrInfo, Des& des, T& obj, Fnc&&,
Reader& r, std::true_type,
OwnershipType<PointerOwnershipType::SharedOwner>) const {
auto& sharedState = ptrInfo.sharedState;
if (!sharedState) {
const auto &ctx = des.template context<TPolymorphicContext<RTTI>>();
const auto& ctx = des.template context<TPolymorphicContext<RTTI>>();
auto prevMemResource = plc.getMemResource();
ctx->deserialize(des, r, TPtrManager<T>::getPtr(obj),
[&obj, &sharedState](typename TPtrManager<T>::TElement *valuePtr) {
sharedState = TPtrManager<T>::createSharedState(valuePtr);
[&obj, &plc, &sharedState, this, prevMemResource](
const std::shared_ptr<PolymorphicHandlerBase>& handler) {
if (_resource) plc.setMemResource(_resource);
sharedState = TPtrManager<T>::createSharedPolymorphic(obj, plc, handler);
if (!_resourcePropagate) plc.setMemResource(prevMemResource);
return TPtrManager<T>::getPtr(obj);
},
[&obj, &plc, this](const std::shared_ptr<PolymorphicHandlerBase>& handler) {
if (_resource) plc.setMemResource(_resource);
TPtrManager<T>::destroyPolymorphic(obj, plc, handler);
});
plc.setMemResource(prevMemResource);
if (!sharedState)
sharedState = TPtrManager<T>::saveToSharedState(obj);
sharedState = TPtrManager<T>::getSharedState(obj);
}
TPtrManager<T>::loadFromSharedState(sharedState.get(), obj);
ptrInfo.processOwner(TPtrManager<T>::getPtr(obj));
}
template<typename Des, typename T, typename Fnc, typename Reader>
void deserializeImpl(PLCInfoDeserializer &ptrInfo, Des &, T &obj, Fnc &&fnc,
Reader &, std::false_type ,
std::integral_constant<PointerOwnershipType, PointerOwnershipType::SharedOwner>) const {
auto &sharedState = ptrInfo.sharedState;
void deserializeImpl(PointerLinkingContext& plc, PLCInfoDeserializer& ptrInfo, Des&, T& obj, Fnc&& fnc,
Reader&, std::false_type, OwnershipType<PointerOwnershipType::SharedOwner>) const {
auto& sharedState = ptrInfo.sharedState;
if (!sharedState) {
if (auto ptr = TPtrManager<T>::getPtr(obj)) {
fnc(*ptr);
sharedState = TPtrManager<T>::saveToSharedState(obj);
auto ptr = TPtrManager<T>::getPtr(obj);
auto prevMemResource = plc.getMemResource();
if (ptr) {
sharedState = TPtrManager<T>::getSharedState(obj);
} else {
auto res = ::bitsery::Access::createInHeap<typename TPtrManager<T>::TElement>();
fnc(*res);
sharedState = TPtrManager<T>::createSharedState(res);
if (_resource) plc.setMemResource(_resource);
sharedState = TPtrManager<T>::createShared(obj, plc,
RTTI::template get<typename TPtrManager<T>::TElement>());
if (!_resourcePropagate) plc.setMemResource(prevMemResource);
ptr = TPtrManager<T>::getPtr(obj);
}
fnc(*ptr);
plc.setMemResource(prevMemResource);
}
TPtrManager<T>::loadFromSharedState(sharedState.get(), obj);
ptrInfo.processOwner(TPtrManager<T>::getPtr(obj));
}
template<typename Des, typename T, typename Fnc, typename Reader, typename isPolymorph>
void deserializeImpl(PLCInfoDeserializer &ptrInfo, Des &des, T &obj, Fnc &&fnc,
Reader &r, isPolymorph polymorph,
std::integral_constant<PointerOwnershipType, PointerOwnershipType::SharedObserver>) const {
deserializeImpl(ptrInfo, des, obj, fnc, r, polymorph,
std::integral_constant<PointerOwnershipType, PointerOwnershipType::SharedOwner>{});
void deserializeImpl(PointerLinkingContext& plc, PLCInfoDeserializer& ptrInfo, Des& des, T& obj,
Fnc&& fnc, Reader& r, isPolymorph polymorph,
OwnershipType<PointerOwnershipType::SharedObserver>) const {
deserializeImpl(plc, ptrInfo, des, obj, fnc, r, polymorph,
OwnershipType<PointerOwnershipType::SharedOwner>{});
}
template<typename Des, typename T, typename Fnc, typename Reader, typename isPolymorphic>
void deserializeImpl(PLCInfoDeserializer &ptrInfo, Des &, T &obj, Fnc &&,
Reader &, isPolymorphic,
std::integral_constant<PointerOwnershipType, PointerOwnershipType::Observer>) const {
ptrInfo.processObserver(reinterpret_cast<void *&>(TPtrManager<T>::getPtrRef(obj)));
void deserializeImpl(PointerLinkingContext&, PLCInfoDeserializer& ptrInfo, Des&, T& obj, Fnc&&,
Reader&, isPolymorphic, OwnershipType<PointerOwnershipType::Observer>) const {
ptrInfo.processObserver(reinterpret_cast<void*&>(TPtrManager<T>::getPtrRef(obj)));
}
PointerType _ptrType;
bool _resourcePropagate;
bitsery::ext::MemResourceBase* _resource;
};
}
//this class is for convenience
class PointerLinkingContext :
public pointer_utils::PointerLinkingContextSerialization,
public pointer_utils::PointerLinkingContextDeserialization {
public:
explicit PointerLinkingContext() = default;
bool isValid() {
return isPointerSerializationValid() && isPointerDeserializationValid();
}
};
}
}

View File

@@ -25,6 +25,7 @@
#include <unordered_map>
#include <memory>
#include "memory_allocator.h"
#include "../../details/adapter_common.h"
#include "../../details/serialization_common.h"
@@ -60,8 +61,12 @@ namespace bitsery {
class PolymorphicHandlerBase {
public:
virtual void *create() const = 0;
virtual void process(void *ser, void *obj) const = 0;
virtual void* create(const PolymorphicAllocator& alloc) const = 0;
virtual void destroy(const PolymorphicAllocator& alloc, void* ptr) const = 0;
virtual void process(void* ser, void* obj) const = 0;
virtual ~PolymorphicHandlerBase() = default;
};
@@ -69,22 +74,26 @@ namespace bitsery {
class PolymorphicHandler : public PolymorphicHandlerBase {
public:
void *create() const final {
return toBase(::bitsery::Access::createInHeap<TDerived>());
void* create(const PolymorphicAllocator& alloc) const final {
return toBase(alloc.allocate<TDerived>(RTTI::template get<TDerived>()));
}
void process(void *ser, void *obj) const final {
static_cast<TSerializer *>(ser)->object(*static_cast<TDerived *>(fromBase(obj)));
void destroy(const PolymorphicAllocator& alloc, void* ptr) const final {
alloc.deallocate<TDerived>(fromBase(ptr), RTTI::template get<TDerived>());
}
void process(void* ser, void* obj) const final {
static_cast<TSerializer*>(ser)->object(*fromBase(obj));
}
private:
void *fromBase(void *obj) const {
return RTTI::template cast<TBase, TDerived>(static_cast<TBase *>(obj));
TDerived* fromBase(void* obj) const {
return RTTI::template cast<TBase, TDerived>(static_cast<TBase*>(obj));
}
void *toBase(void *obj) const {
return RTTI::template cast<TDerived, TBase>(static_cast<TDerived *>(obj));
TBase* toBase(void* obj) const {
return RTTI::template cast<TDerived, TBase>(static_cast<TDerived*>(obj));
}
};
@@ -98,13 +107,13 @@ namespace bitsery {
std::size_t baseHash;
std::size_t derivedHash;
bool operator==(const BaseToDerivedKey &other) const {
bool operator==(const BaseToDerivedKey& other) const {
return baseHash == other.baseHash && derivedHash == other.derivedHash;
}
};
struct BaseToDerivedKeyHashier {
size_t operator()(const BaseToDerivedKey &key) const {
size_t operator()(const BaseToDerivedKey& key) const {
return (key.baseHash + (key.baseHash << 6) + (key.derivedHash >> 2)) ^ key.derivedHash;
}
};
@@ -132,8 +141,9 @@ namespace bitsery {
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)
if (_baseToDerivedMap
.emplace(key, std::make_shared<PolymorphicHandler<RTTI, TSerializer, TBase, TDerived>>())
.second)
_baseToDerivedArray[key.baseHash].push_back(key.derivedHash);
}
@@ -142,7 +152,7 @@ namespace bitsery {
//cannot add abstract class
}
std::unordered_map<BaseToDerivedKey, std::unique_ptr<PolymorphicHandlerBase>, BaseToDerivedKeyHashier> _baseToDerivedMap{};
std::unordered_map<BaseToDerivedKey, std::shared_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.
@@ -156,13 +166,14 @@ namespace bitsery {
}
template<typename TSerializer, template<typename> class THierarchy = PolymorphicBaseClass, typename T1, typename ...Tn>
[[deprecated("de/serializer instance is not required")]] void registerBasesList(const TSerializer &s, PolymorphicClassesList<T1, Tn...>) {
[[deprecated("de/serializer instance is not required")]] void
registerBasesList(const TSerializer& s, PolymorphicClassesList<T1, Tn...>) {
add<TSerializer, THierarchy, T1, T1>();
registerBasesList<TSerializer, THierarchy>(s, PolymorphicClassesList<Tn...>{});
}
template<typename TSerializer, template<typename> class THierarchy>
[[deprecated]] void registerBasesList(const TSerializer &, PolymorphicClassesList<>) {
[[deprecated]] void registerBasesList(const TSerializer&, PolymorphicClassesList<>) {
}
// THierarchy is the name of class, that defines hierarchy
@@ -180,7 +191,7 @@ namespace bitsery {
}
// optional method, in case you want to construct base class hierarchy your self
template <typename TSerializer, typename TBase, typename TDerived>
template<typename TSerializer, typename TBase, typename TDerived>
void registerSingleBaseBranch() {
static_assert(std::is_base_of<TBase, TDerived>::value, "TDerived must be derived from TBase");
static_assert(!std::is_abstract<TDerived>::value, "TDerived cannot be abstract");
@@ -189,14 +200,14 @@ namespace bitsery {
template<typename Serializer, typename Writer, typename TBase>
void serialize(Serializer &ser, Writer &writer, TBase &obj) {
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& 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);
@@ -205,8 +216,9 @@ namespace bitsery {
it->second->process(&ser, &obj);
}
template<typename Deserializer, typename Reader, typename TBase, typename TAssignFnc>
void deserialize(Deserializer &des, Reader &reader, TBase *obj, TAssignFnc assignFnc) {
template<typename Deserializer, typename Reader, typename TBase, typename TCreateFnc, typename TDestroyFnc>
void deserialize(Deserializer& des, Reader& reader, TBase* obj,
TCreateFnc createFnc, TDestroyFnc destroyFnc) {
size_t derivedIndex{};
details::readSize(reader, derivedIndex, std::numeric_limits<size_t>::max());
@@ -217,18 +229,29 @@ namespace bitsery {
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;
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);
if (obj) {
destroyFnc(getPolymorphicHandler(*obj));
}
obj = createFnc(handler);
}
handler->process(&des, obj);
} else
reader.setError(ReaderError::InvalidPointer);
}
template<typename TBase>
const std::shared_ptr<PolymorphicHandlerBase>& getPolymorphicHandler(TBase& obj) const {
auto deleteHandlerIt = _baseToDerivedMap.find(
BaseToDerivedKey{RTTI::template get<TBase>(), RTTI::template get<TBase>(obj)});
assert(deleteHandlerIt != _baseToDerivedMap.end());
return deleteHandlerIt->second;
}
};
}

View File

@@ -37,7 +37,7 @@ namespace bitsery {
// !std::is_volatile<TBase>::value, "");
template<typename TBase>
static size_t get(TBase &obj) {
static size_t get(TBase& obj) {
return typeid(obj).hash_code();
}
@@ -47,9 +47,9 @@ namespace bitsery {
}
template<typename TBase, typename TDerived>
static constexpr TDerived *cast(TBase *obj) {
static constexpr TDerived* cast(TBase* obj) {
static_assert(!std::is_pointer<TDerived>::value, "");
return dynamic_cast<TDerived *>(obj);
return dynamic_cast<TDerived*>(obj);
}
template<typename TBase>

View File

@@ -29,54 +29,38 @@
namespace bitsery {
namespace flexible {
//overload when T is reference type
template<typename S, typename T>
void archiveProcessImpl(S &s, T &&head, std::true_type) {
s.object(std::forward<T>(head));
}
//overload when T is rvalue type, only allowable for behaviour modifying functions for deserializer
template<typename S, typename T>
void archiveProcessImpl(S &s, T &&head, std::false_type) {
static_assert(std::is_base_of<ArchiveWrapperFnc, T>::value,
"\nOnly archive behaviour modifying functions can be passed by rvalue to deserializer\n");
serialize(s, head);
}
}
//define function that enables s.archive(....) usage
template<typename S, typename T>
void archiveProcess(S &s, T &&head) {
flexible::archiveProcessImpl(s, std::forward<T>(head), std::is_reference<T>{});
void archiveProcess(S& s, T&& head) {
static_assert(std::is_lvalue_reference<T>::value || std::is_base_of<flexible::ArchiveWrapperFnc, T>::value,
"Argument must be either lvalue or subclass of flexible::ArchiveWrapperFnc");
s.object(head);
}
//wrapper functions that enables to serialize as container or string
template<typename T, size_t N>
flexible::CArray<T, N, true> asText(T (&str)[N]) {
flexible::CArray<T, N, true> asText(T (& str)[N]) {
return {str};
}
template<typename T, size_t N>
flexible::CArray<T, N, false> asContainer(T (&obj)[N]) {
flexible::CArray<T, N, false> asContainer(T (& obj)[N]) {
return {obj};
}
template <typename T>
template<typename T>
flexible::MaxSize<T> maxSize(T& obj, size_t max) {
return {obj, max};
}
//define serialize function for fundamental types
template<typename S>
void serialize(S &s, bool &v) {
void serialize(S& s, bool& v) {
s.boolValue(v);
}
template<typename S, typename T, typename std::enable_if<details::IsFundamentalType<T>::value>::type * = nullptr>
void serialize(S &s, T &v) {
void serialize(S& s, T& v) {
s.template value<sizeof(T)>(v);
}
@@ -84,18 +68,18 @@ namespace bitsery {
//if array is integral type, specify explicitly how to process: as text or container
template<typename S, typename T, size_t N, typename std::enable_if<std::is_integral<T>::value>::type * = nullptr>
void serialize(S &, T (&)[N]) {
void serialize(S&, T (&)[N]) {
static_assert(N == 0,
"\nPlease use 'asText(obj)' or 'asContainer(obj)' when using c-style array with integral types\n");
}
template<typename S, typename T, size_t N, typename std::enable_if<!std::is_integral<T>::value>::type * = nullptr>
void serialize(S &s, T (&obj)[N]) {
void serialize(S& s, T (& obj)[N]) {
flexible::processContainer(s, obj);
}
//this is a helper class that enforce fundamental type sizes, when used on multiple platforms
template <size_t TShort, size_t TInt, size_t TLong, size_t TLongLong>
template<size_t TShort, size_t TInt, size_t TLong, size_t TLongLong>
void assertFundamentalTypeSizes() {
//http://en.cppreference.com/w/cpp/language/types
static_assert(sizeof(short) == TShort, "");

View File

@@ -315,9 +315,11 @@ TEST(DeserializeNonDefaultConstructible, PolymorphicPointerAndSmartPointer) {
data.wp = data.sp;
PolymorphicPointers res{};
TContext serCtx{};
std::get<1>(serCtx).registerBasesList<typename SerContext::TSerializer>(bitsery::ext::PolymorphicClassesList<PolymorphicNDCBase>{});
TContext desCtx{};
std::get<1>(serCtx).registerBasesList<typename SerContext::TSerializer>(bitsery::ext::PolymorphicClassesList<PolymorphicNDCBase>{});
std::get<1>(desCtx).registerBasesList<typename SerContext::TDeserializer>(bitsery::ext::PolymorphicClassesList<PolymorphicNDCBase>{});
ctx.createSerializer(&serCtx).object(data);
@@ -343,4 +345,6 @@ TEST(DeserializeNonDefaultConstructible, PolymorphicPointerAndSmartPointer) {
EXPECT_THAT(*resup, Eq(*dataup));
EXPECT_THAT(*ressp, Eq(*datasp));
EXPECT_THAT(*reswp, Eq(*datawp));
std::get<0>(serCtx).clearSharedState();
std::get<0>(desCtx).clearSharedState();
}

View File

@@ -0,0 +1,364 @@
//MIT License
//
//Copyright (c) 2019 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#include <bitsery/ext/inheritance.h>
#include <bitsery/ext/pointer.h>
#include <bitsery/ext/std_smart_ptr.h>
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
using bitsery::ext::BaseClass;
using bitsery::ext::VirtualBaseClass;
using bitsery::ext::InheritanceContext;
using bitsery::ext::PointerLinkingContext;
using bitsery::ext::PolymorphicContext;
using bitsery::ext::StandardRTTI;
using bitsery::ext::PointerOwner;
using bitsery::ext::PointerObserver;
using bitsery::ext::ReferencedByPointer;
using bitsery::ext::StdSmartPtr;
using testing::Eq;
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 {
Base() = default;
explicit Base(uint64_t v) : x{v} {}
uint64_t x{};
virtual ~Base() = default;
};
template<typename S>
void serialize(S& s, Base& o) {
s.value8b(o.x);
}
struct Derived1 : Base {
Derived1() = default;
Derived1(uint64_t x_, uint64_t y_) : Base{x_}, y1{y_} {}
friend bool operator==(const Derived1& lhs, const Derived1& rhs) {
return lhs.x == rhs.x && lhs.y1 == rhs.y1;
}
uint64_t y1{};
};
template<typename S>
void serialize(S& s, Derived1& o) {
s.ext(o, BaseClass<Base>{});
s.value8b(o.y1);
}
struct Derived2 : Base {
uint64_t y1{};
uint64_t y2{};
};
template<typename S>
void serialize(S& s, Derived2& o) {
s.ext(o, BaseClass<Base>{});
s.value8b(o.y1);
s.value8b(o.y2);
}
// polymorphic structure that contains polymorphic pointer, to test memory resource propagation
struct PolyPtrWithPolyPtrBase {
std::unique_ptr<Base> ptr{};
virtual ~PolyPtrWithPolyPtrBase() = default;
};
template<typename S>
void serialize(S& s, PolyPtrWithPolyPtrBase& o) {
s.ext(o.ptr, StdSmartPtr{});
}
struct DerivedPolyPtrWithPolyPtr : PolyPtrWithPolyPtrBase {
};
template<typename S>
void serialize(S& s, DerivedPolyPtrWithPolyPtr& o) {
s.ext(o.ptr, StdSmartPtr{});
}
//define relationships between base class and derived classes for runtime polymorphism
namespace bitsery {
namespace ext {
template<>
struct PolymorphicBaseClass<Base> : PolymorphicDerivedClasses<Derived1, Derived2> {
};
template<>
struct PolymorphicBaseClass<PolyPtrWithPolyPtrBase> : PolymorphicDerivedClasses<DerivedPolyPtrWithPolyPtr> {
};
}
}
// this class is for testing
struct TestAllocInfo {
void* ptr;
size_t bytes;
size_t alignment;
size_t typeId;
friend bool operator==(const TestAllocInfo& lhs, const TestAllocInfo& rhs) {
return std::tie(lhs.ptr, lhs.bytes, lhs.alignment, lhs.typeId) ==
std::tie(rhs.ptr, rhs.bytes, rhs.alignment, rhs.typeId);
}
};
struct MemResourceForTest : public bitsery::ext::MemResourceBase {
void* allocate(size_t bytes, size_t alignment, size_t typeId) override {
const auto res = bitsery::ext::MemResourceNewDelete{}.allocate(bytes, alignment, typeId);
allocs.push_back({res, bytes, alignment, typeId});
return res;
}
void deallocate(void* ptr, size_t bytes, size_t alignment, size_t typeId) override {
deallocs.push_back({ptr, bytes, alignment, typeId});
bitsery::ext::MemResourceNewDelete{}.deallocate(ptr, bytes, alignment, typeId);
}
std::vector<TestAllocInfo> allocs{};
std::vector<TestAllocInfo> deallocs{};
};
class SerializeExtensionPointerWithAllocator : public testing::Test {
public:
TContext plctx{};
SerContext sctx{};
typename SerContext::TSerializer& createSerializer() {
auto& res = sctx.createSerializer(&plctx);
std::get<2>(plctx).clear();
//bind serializer with classes
std::get<2>(plctx).registerBasesList<SerContext::TSerializer>(
bitsery::ext::PolymorphicClassesList<Base, PolyPtrWithPolyPtrBase>{});
return res;
}
typename SerContext::TDeserializer& createDeserializer() {
auto& res = sctx.createDeserializer(&plctx);
std::get<2>(plctx).clear();
//bind deserializer with classes
std::get<2>(plctx).registerBasesList<SerContext::TDeserializer>(
bitsery::ext::PolymorphicClassesList<Base, PolyPtrWithPolyPtrBase>{});
return res;
}
bool isPointerContextValid() {
return std::get<0>(plctx).isValid();
}
virtual void TearDown() override {
EXPECT_TRUE(isPointerContextValid());
}
};
TEST_F(SerializeExtensionPointerWithAllocator, CanSetDefaultMemoryResourceInPointerLinkingContext) {
MemResourceForTest memRes{};
std::get<0>(plctx).setMemResource(&memRes);
Base* baseData = new Derived1{2, 1};
createSerializer().ext(baseData, PointerOwner{});
Base* baseRes = nullptr;
createDeserializer().ext(baseRes, PointerOwner{});
auto dData = dynamic_cast<Derived1*>(baseData);
auto dRes = dynamic_cast<Derived1*>(baseRes);
EXPECT_THAT(dRes, ::testing::NotNull());
EXPECT_THAT(*dData, *dRes);
EXPECT_THAT(memRes.allocs.size(), Eq(1u));
EXPECT_THAT(memRes.allocs[0].bytes, Eq(sizeof(Derived1)));
EXPECT_THAT(memRes.allocs[0].alignment, Eq(alignof(Derived1)));
EXPECT_THAT(memRes.allocs[0].typeId, Eq(bitsery::ext::StandardRTTI::get<Derived1>()));
EXPECT_THAT(memRes.deallocs.size(), Eq(0u));
delete dData;
delete dRes;
}
TEST_F(SerializeExtensionPointerWithAllocator, CorrectlyDeallocatesPreviousInstance) {
MemResourceForTest memRes{};
std::get<0>(plctx).setMemResource(&memRes);
Base* baseData = new Derived1{2, 1};
createSerializer().ext(baseData, PointerOwner{});
Base* baseRes = new Derived2;
createDeserializer().ext(baseRes, PointerOwner{});
auto dData = dynamic_cast<Derived1*>(baseData);
auto dRes = dynamic_cast<Derived1*>(baseRes);
EXPECT_THAT(dRes, ::testing::NotNull());
EXPECT_THAT(*dData, *dRes);
EXPECT_THAT(memRes.allocs.size(), Eq(1u));
EXPECT_THAT(memRes.allocs[0].bytes, Eq(sizeof(Derived1)));
EXPECT_THAT(memRes.allocs[0].alignment, Eq(alignof(Derived1)));
EXPECT_THAT(memRes.allocs[0].typeId, Eq(bitsery::ext::StandardRTTI::get<Derived1>()));
EXPECT_THAT(memRes.deallocs.size(), Eq(1u));
EXPECT_THAT(memRes.deallocs[0].bytes, Eq(sizeof(Derived2)));
EXPECT_THAT(memRes.deallocs[0].alignment, Eq(alignof(Derived2)));
EXPECT_THAT(memRes.deallocs[0].typeId, Eq(bitsery::ext::StandardRTTI::get<Derived2>()));
delete dData;
delete dRes;
}
TEST_F(SerializeExtensionPointerWithAllocator, DefaultDeleterIsNotUsedForStdUniquePtr) {
MemResourceForTest memRes{};
std::get<0>(plctx).setMemResource(&memRes);
std::unique_ptr<Base> baseData{};
createSerializer().ext(baseData, StdSmartPtr{});
auto baseRes = std::unique_ptr<Base>(new Derived1{45, 64});
createDeserializer().ext(baseRes, StdSmartPtr{});
EXPECT_THAT(memRes.allocs.size(), Eq(0u));
EXPECT_THAT(memRes.deallocs.size(), Eq(1u));
EXPECT_THAT(memRes.deallocs[0].bytes, Eq(sizeof(Derived1)));
EXPECT_THAT(memRes.deallocs[0].alignment, Eq(alignof(Derived1)));
EXPECT_THAT(memRes.deallocs[0].typeId, Eq(bitsery::ext::StandardRTTI::get<Derived1>()));
}
struct CustomBaseDeleter {
void operator()(Base* obj) {
delete obj;
}
};
TEST_F(SerializeExtensionPointerWithAllocator, CustomDeleterIsUsedForStdUniquePtr) {
MemResourceForTest memRes{};
std::get<0>(plctx).setMemResource(&memRes);
std::unique_ptr<Base, CustomBaseDeleter> baseData{};
createSerializer().ext(baseData, StdSmartPtr{});
auto baseRes = std::unique_ptr<Base, CustomBaseDeleter>(new Derived1{45, 64});
createDeserializer().ext(baseRes, StdSmartPtr{});
EXPECT_THAT(memRes.allocs.size(), Eq(0u));
EXPECT_THAT(memRes.deallocs.size(), Eq(0u));
}
TEST_F(SerializeExtensionPointerWithAllocator, CanSetMemResourcePerPointer) {
MemResourceForTest memRes1{};
MemResourceForTest memRes2{};
std::get<0>(plctx).setMemResource(&memRes1);
Base* baseData = new Derived1{2, 1};
createSerializer().ext(baseData, PointerOwner{bitsery::ext::PointerType::Nullable, &memRes2});
Base* baseRes = new Derived2;
createDeserializer().ext(baseRes, PointerOwner{bitsery::ext::PointerType::Nullable, &memRes2});
auto dData = dynamic_cast<Derived1*>(baseData);
auto dRes = dynamic_cast<Derived1*>(baseRes);
EXPECT_THAT(dRes, ::testing::NotNull());
EXPECT_THAT(*dData, *dRes);
EXPECT_THAT(memRes1.allocs.size(), Eq(0u));
EXPECT_THAT(memRes1.deallocs.size(), Eq(0u));
EXPECT_THAT(memRes2.allocs.size(), Eq(1u));
EXPECT_THAT(memRes2.allocs[0].bytes, Eq(sizeof(Derived1)));
EXPECT_THAT(memRes2.allocs[0].alignment, Eq(alignof(Derived1)));
EXPECT_THAT(memRes2.allocs[0].typeId, Eq(bitsery::ext::StandardRTTI::get<Derived1>()));
EXPECT_THAT(memRes2.deallocs.size(), Eq(1u));
EXPECT_THAT(memRes2.deallocs[0].bytes, Eq(sizeof(Derived2)));
EXPECT_THAT(memRes2.deallocs[0].alignment, Eq(alignof(Derived2)));
EXPECT_THAT(memRes2.deallocs[0].typeId, Eq(bitsery::ext::StandardRTTI::get<Derived2>()));
delete dData;
delete dRes;
}
TEST_F(SerializeExtensionPointerWithAllocator, MemResourceSetPerPointerByDefaultDoNotPropagate) {
MemResourceForTest memRes1{};
MemResourceForTest memRes2{};
std::get<0>(plctx).setMemResource(&memRes1);
auto data = std::unique_ptr<PolyPtrWithPolyPtrBase>(new PolyPtrWithPolyPtrBase{});
data->ptr = std::unique_ptr<Base>(new Derived1{5, 6});
createSerializer().ext(data, StdSmartPtr{bitsery::ext::PointerType::Nullable, &memRes2});
auto res = std::unique_ptr<PolyPtrWithPolyPtrBase>(new DerivedPolyPtrWithPolyPtr{});
res->ptr = std::unique_ptr<Base>(new Derived2{});
createDeserializer().ext(res, StdSmartPtr{bitsery::ext::PointerType::Nullable, &memRes2});
EXPECT_THAT(memRes1.allocs.size(), Eq(1u));
// Base* was destroyed by unique_ptr on PolyPtrWithPolyPtrBase destructor, hence == 0
EXPECT_THAT(memRes1.deallocs.size(), Eq(0u));
EXPECT_THAT(memRes2.allocs.size(), Eq(1u));
EXPECT_THAT(memRes2.deallocs.size(), Eq(1u));
}
TEST_F(SerializeExtensionPointerWithAllocator, MemResourceSetPerPointerCanPropagate) {
MemResourceForTest memRes1{};
MemResourceForTest memRes2{};
std::get<0>(plctx).setMemResource(&memRes1);
auto data = std::unique_ptr<PolyPtrWithPolyPtrBase>(new PolyPtrWithPolyPtrBase{});
data->ptr = std::unique_ptr<Base>(new Derived1{5, 6});
createSerializer().ext(data, StdSmartPtr{bitsery::ext::PointerType::Nullable, &memRes2, true});
auto res = std::unique_ptr<PolyPtrWithPolyPtrBase>(new DerivedPolyPtrWithPolyPtr{});
res->ptr = std::unique_ptr<Base>(new Derived2{});
createDeserializer().ext(res, StdSmartPtr{bitsery::ext::PointerType::Nullable, &memRes2, true});
EXPECT_THAT(memRes1.allocs.size(), Eq(0u));
EXPECT_THAT(memRes1.deallocs.size(), Eq(0u));
EXPECT_THAT(memRes2.allocs.size(), Eq(2u));
// deallocates are actually == 1, because when we destroy PolyPtrWithPolyPtrBase
// it also destroys Base because it is managed by unique_ptr.
// in order to do it correctly we should always use custom deleter for structures with nested pointers
EXPECT_THAT(memRes2.deallocs.size(), Eq(1u));
}

View File

@@ -49,7 +49,7 @@ struct Base {
};
template<typename S>
void serialize(S &s, Base &o) {
void serialize(S& s, Base& o) {
s.value1b(o.x);
}
@@ -65,7 +65,7 @@ struct Derived : virtual Base {
};
template<typename S>
void serialize(S &s, Derived &o) {
void serialize(S& s, Derived& o) {
s.ext(o, VirtualBaseClass<Base>{});
s.value1b(o.y);
}
@@ -82,7 +82,7 @@ struct MoreDerived : Derived {
};
template<typename S>
void serialize(S &s, MoreDerived &o) {
void serialize(S& s, MoreDerived& o) {
s.ext(o, BaseClass<Derived>{});
s.value1b(o.z);
}
@@ -120,13 +120,13 @@ public:
TContext plctx{};
SerContext sctx{};
typename SerContext::TSerializer &createSerializer() {
auto &res = sctx.createSerializer(&plctx);
typename SerContext::TSerializer& createSerializer() {
auto& res = sctx.createSerializer(&plctx);
return res;
}
typename SerContext::TDeserializer &createDeserializer() {
auto &res = sctx.createDeserializer(&plctx);
typename SerContext::TDeserializer& createDeserializer() {
auto& res = sctx.createDeserializer(&plctx);
return res;
}
@@ -156,19 +156,21 @@ public:
TContext plctx{};
SerContext sctx{};
typename SerContext::TSerializer &createSerializer() {
auto &res = sctx.createSerializer(&plctx);
typename SerContext::TSerializer& createSerializer() {
auto& res = sctx.createSerializer(&plctx);
std::get<2>(plctx).clear();
//bind serializer with classes
std::get<2>(plctx).template registerBasesList<SerContext::TSerializer>(bitsery::ext::PolymorphicClassesList<Base>{});
std::get<2>(plctx).template registerBasesList<SerContext::TSerializer>(
bitsery::ext::PolymorphicClassesList<Base>{});
return res;
}
typename SerContext::TDeserializer &createDeserializer() {
auto &res = sctx.createDeserializer(&plctx);
typename SerContext::TDeserializer& createDeserializer() {
auto& res = sctx.createDeserializer(&plctx);
std::get<2>(plctx).clear();
//bind deserializer with classes
std::get<2>(plctx).template registerBasesList<SerContext::TDeserializer>(bitsery::ext::PolymorphicClassesList<Base>{});
std::get<2>(plctx).template registerBasesList<SerContext::TDeserializer>(
bitsery::ext::PolymorphicClassesList<Base>{});
return res;
}
@@ -194,14 +196,14 @@ struct SharedPtrTest {
};
using TestingWithNonPolymorphicTypes = ::testing::Types<
UniquePtrTest,
SharedPtrTest>;
UniquePtrTest,
SharedPtrTest>;
TYPED_TEST_CASE(SerializeExtensionStdSmartPtrNonPolymorphicType, TestingWithNonPolymorphicTypes);
using TestingWithPolymorphicTypes = ::testing::Types<
UniquePtrTest,
SharedPtrTest>;
UniquePtrTest,
SharedPtrTest>;
TYPED_TEST_CASE(SerializeExtensionStdSmartPtrPolymorphicType, TestingWithPolymorphicTypes);
@@ -266,14 +268,14 @@ TYPED_TEST(SerializeExtensionStdSmartPtrNonPolymorphicType, CanUseLambdaOverload
using Ext = typename TestFixture::TExt;
Ptr data{new MyStruct1{3, 78}};
auto &ser = this->createSerializer();
ser.ext(data, Ext{}, [&ser](MyStruct1 &o) {
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) {
auto& des = this->createDeserializer();
des.ext(res, Ext{}, [&des](MyStruct1& o) {
des.value4b(o.i1);
});
@@ -297,13 +299,13 @@ TYPED_TEST(SerializeExtensionStdSmartPtrNonPolymorphicType, FirstPtrThenPointerO
using Ext = typename TestFixture::TExt;
Ptr data{new uint16_t{3}};
uint16_t *dataObs = data.get();
auto &ser = this->createSerializer();
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();
uint16_t* resObs = nullptr;
auto& des = this->createDeserializer();
des.ext2b(res, Ext{});
des.ext2b(resObs, PointerObserver{});
@@ -315,13 +317,13 @@ TYPED_TEST(SerializeExtensionStdSmartPtrNonPolymorphicType, FirstPointerObserver
using Ext = typename TestFixture::TExt;
Ptr data{new uint16_t{3}};
uint16_t *dataObs = data.get();
auto &ser = this->createSerializer();
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();
uint16_t* resObs = nullptr;
auto& des = this->createDeserializer();
des.ext2b(resObs, PointerObserver{});
des.ext2b(res, Ext{});
EXPECT_THAT(resObs, Eq(res.get()));
@@ -363,8 +365,8 @@ TYPED_TEST(SerializeExtensionStdSmartPtrPolymorphicType, Data1Result0) {
Ptr baseRes{};
this->createDeserializer().ext(baseRes, Ext{});
auto *data = dynamic_cast<Derived *>(baseData.get());
auto *res = dynamic_cast<Derived *>(baseRes.get());
auto* data = dynamic_cast<Derived*>(baseData.get());
auto* res = dynamic_cast<Derived*>(baseRes.get());
EXPECT_THAT(data, ::testing::NotNull());
EXPECT_THAT(res, ::testing::NotNull());
@@ -381,8 +383,8 @@ TYPED_TEST(SerializeExtensionStdSmartPtrPolymorphicType, DataAndResultWithDiffer
Ptr baseRes{new Base{}};
this->createDeserializer().ext(baseRes, Ext{});
auto *data = dynamic_cast<Derived *>(baseData.get());
auto *res = dynamic_cast<Derived *>(baseRes.get());
auto* data = dynamic_cast<Derived*>(baseData.get());
auto* res = dynamic_cast<Derived*>(baseRes.get());
EXPECT_THAT(data, ::testing::NotNull());
EXPECT_THAT(res, ::testing::NotNull());
@@ -403,16 +405,16 @@ public:
TContext plctx{};
SerContext sctx{};
typename SerContext::TSerializer &createSerializer() {
auto &res = sctx.createSerializer(&plctx);
typename SerContext::TSerializer& createSerializer() {
auto& res = sctx.createSerializer(&plctx);
std::get<2>(plctx).clear();
//bind serializer with classes
std::get<2>(plctx).registerBasesList<SerContext::TSerializer>(bitsery::ext::PolymorphicClassesList<Base>{});
return res;
}
typename SerContext::TDeserializer &createDeserializer() {
auto &res = sctx.createDeserializer(&plctx);
typename SerContext::TDeserializer& createDeserializer() {
auto& res = sctx.createDeserializer(&plctx);
std::get<2>(plctx).clear();
//bind deserializer with classes
std::get<2>(plctx).registerBasesList<SerContext::TDeserializer>(bitsery::ext::PolymorphicClassesList<Base>{});
@@ -436,10 +438,10 @@ TEST_F(SerializeExtensionStdSmartSharedPtr, SameSharedObjectIsSerializedOnce) {
std::shared_ptr<Base> baseData1{new Derived{3, 78}};
std::shared_ptr<Base> baseData2{baseData1};
auto &ser = createSerializer();
auto& ser = createSerializer();
ser.ext(baseData1, StdSmartPtr{});
ser.ext(baseData1, StdSmartPtr{});
auto &des = createDeserializer();
auto& des = createDeserializer();
//1b linking context (for 1st time)
//1b dynamic type info
@@ -453,10 +455,10 @@ TEST_F(SerializeExtensionStdSmartSharedPtr, PointerLinkingContextCorrectlyClearS
std::shared_ptr<Base> baseData1{new Derived{3, 78}};
auto &ser = createSerializer();
auto& ser = createSerializer();
ser.ext(baseData1, StdSmartPtr{});
std::shared_ptr<Base> baseRes1{};
auto &des = createDeserializer();
auto& des = createDeserializer();
des.ext(baseRes1, StdSmartPtr{});
EXPECT_THAT(baseRes1.use_count(), Eq(2));
clearSharedState();
@@ -469,7 +471,7 @@ 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();
auto& ser = createSerializer();
ser.ext(baseData1, StdSmartPtr{});
ser.ext(baseData2, StdSmartPtr{});
ser.ext(baseData21, StdSmartPtr{});
@@ -477,12 +479,12 @@ TEST_F(SerializeExtensionStdSmartSharedPtr, CorrectlyManagesSameSharedObject) {
std::shared_ptr<Base> baseRes1{};
std::shared_ptr<Base> baseRes2{};
std::shared_ptr<Base> baseRes21{};
auto &des = createDeserializer();
auto& des = createDeserializer();
des.ext(baseRes1, StdSmartPtr{});
des.ext(baseRes2, StdSmartPtr{});
des.ext(baseRes21, StdSmartPtr{});
auto *data = dynamic_cast<Derived *>(baseRes1.get());
auto* data = dynamic_cast<Derived*>(baseRes1.get());
EXPECT_THAT(data, ::testing::NotNull());
clearSharedState();
@@ -500,7 +502,7 @@ 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();
auto& ser = createSerializer();
ser.ext(baseData1, StdSmartPtr{});
ser.ext(baseData11, StdSmartPtr{});
ser.ext(baseData12, StdSmartPtr{});
@@ -508,12 +510,12 @@ TEST_F(SerializeExtensionStdSmartSharedPtr, FirstSharedThenWeakPtr) {
std::shared_ptr<Base> baseRes1{};
std::weak_ptr<Base> baseRes11{};
std::weak_ptr<Base> baseRes12{};
auto &des = createDeserializer();
auto& des = createDeserializer();
des.ext(baseRes1, StdSmartPtr{});
des.ext(baseRes11, StdSmartPtr{});
des.ext(baseRes12, StdSmartPtr{});
auto *data = dynamic_cast<Derived *>(baseRes1.get());
auto* data = dynamic_cast<Derived*>(baseRes1.get());
EXPECT_THAT(data, ::testing::NotNull());
clearSharedState();
@@ -528,27 +530,26 @@ TEST_F(SerializeExtensionStdSmartSharedPtr, FirstSharedThenWeakPtr) {
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();
std::shared_ptr<MyStruct1> baseData1{new MyStruct1{3, 78}};
std::weak_ptr<MyStruct1> baseData11{baseData1};
std::weak_ptr<MyStruct1> baseData2{};
auto& ser = createSerializer();
ser.ext(baseData2, StdSmartPtr{});
ser.ext(baseData11, StdSmartPtr{});
ser.ext(baseData1, StdSmartPtr{});
std::shared_ptr<Base> baseRes1{};
std::weak_ptr<Base> baseRes11{};
std::weak_ptr<Base> baseRes2{};
auto &des = createDeserializer();
std::shared_ptr<MyStruct1> baseRes1{};
std::weak_ptr<MyStruct1> baseRes11{};
std::weak_ptr<MyStruct1> baseRes2{};
auto& des = createDeserializer();
des.ext(baseRes2, StdSmartPtr{});
des.ext(baseRes11, StdSmartPtr{});
des.ext(baseRes1, StdSmartPtr{});
auto *data = dynamic_cast<Derived *>(baseRes1.get());
EXPECT_THAT(data, ::testing::NotNull());
clearSharedState();
EXPECT_THAT(*baseData1, Eq(*baseRes1));
EXPECT_THAT(baseRes1.use_count(), Eq(1));
EXPECT_THAT(baseRes2.use_count(), Eq(0));
EXPECT_THAT(baseRes11.use_count(), Eq(1));
@@ -557,13 +558,59 @@ TEST_F(SerializeExtensionStdSmartSharedPtr, FirstWeakThenSharedPtr) {
EXPECT_TRUE(isPointerContextValid());
}
TEST_F(SerializeExtensionStdSmartSharedPtr, WeakPtrFirstPolymorphicData0Result1) {
std::shared_ptr<Base> baseData1{};
std::weak_ptr<Base> baseData2{};
auto& ser = createSerializer();
ser.ext(baseData2, StdSmartPtr{});
ser.ext(baseData1, StdSmartPtr{});
std::shared_ptr<Base> baseRes1{new Base{}};
std::weak_ptr<Base> baseRes2{baseRes1};
auto& des = createDeserializer();
des.ext(baseRes2, StdSmartPtr{});
des.ext(baseRes1, StdSmartPtr{});
clearSharedState();
EXPECT_THAT(baseRes1.use_count(), Eq(0));
EXPECT_THAT(baseRes2.use_count(), Eq(0));
baseRes1.reset();
EXPECT_TRUE(isPointerContextValid());
}
TEST_F(SerializeExtensionStdSmartSharedPtr, WeakPtrFirstNonPolymorphicData0Result1) {
std::shared_ptr<MyStruct2> baseData1{};
std::weak_ptr<MyStruct2> baseData2{};
auto& ser = createSerializer();
ser.ext(baseData2, StdSmartPtr{});
ser.ext(baseData1, StdSmartPtr{});
std::shared_ptr<MyStruct2> baseRes1{new MyStruct2{MyStruct2::MyEnum::V4, {1, 87}}};
std::weak_ptr<MyStruct2> baseRes2{baseRes1};
auto& des = createDeserializer();
des.ext(baseRes2, StdSmartPtr{});
des.ext(baseRes1, StdSmartPtr{});
clearSharedState();
EXPECT_THAT(baseRes1.use_count(), Eq(0));
EXPECT_THAT(baseRes2.use_count(), Eq(0));
baseRes1.reset();
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();
auto& ser = createSerializer();
ser.ext(baseData1, StdSmartPtr{});
ser.ext(baseData2, StdSmartPtr{});
ser.ext(baseData3, StdSmartPtr{});
@@ -573,7 +620,7 @@ TEST_F(SerializeExtensionStdSmartSharedPtr, FewPtrsAreEmpty) {
std::shared_ptr<Base> baseRes2{new Derived{3, 78}};
std::weak_ptr<Base> baseRes3{baseRes2};
std::weak_ptr<Base> baseRes11{};
auto &des = createDeserializer();
auto& des = createDeserializer();
des.ext(baseRes1, StdSmartPtr{});
des.ext(baseRes2, StdSmartPtr{});
des.ext(baseRes3, StdSmartPtr{});
@@ -590,14 +637,15 @@ TEST_F(SerializeExtensionStdSmartSharedPtr, FewPtrsAreEmpty) {
EXPECT_TRUE(isPointerContextValid());
}
TEST_F(SerializeExtensionStdSmartSharedPtr, WhenResultObjectExistsSameType) {
std::shared_ptr<Base> baseData1{new Derived{3, 78}};
auto &ser = createSerializer();
auto& ser = createSerializer();
ser.ext(baseData1, StdSmartPtr{});
std::shared_ptr<Base> baseRes1{new Derived{0, 0}};
auto &des = createDeserializer();
auto& des = createDeserializer();
des.ext(baseRes1, StdSmartPtr{});
clearSharedState();
@@ -610,25 +658,25 @@ TEST_F(SerializeExtensionStdSmartSharedPtr, WhenResultObjectExistsSameType) {
TEST_F(SerializeExtensionStdSmartSharedPtr, WhenResultObjectExistsDifferentType) {
std::shared_ptr<Base> baseData1{new Derived{3, 78}};
auto &ser = createSerializer();
auto& ser = createSerializer();
ser.ext(baseData1, StdSmartPtr{});
std::shared_ptr<Base> baseRes1{new Base{}};
auto &des = createDeserializer();
auto& des = createDeserializer();
des.ext(baseRes1, StdSmartPtr{});
clearSharedState();
EXPECT_THAT(baseRes1.use_count(), Eq(1));
EXPECT_THAT(baseRes1->x, Eq(baseData1->x));
EXPECT_THAT(dynamic_cast<Derived *>(baseRes1.get()), ::testing::NotNull());
EXPECT_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();
auto& ser = createSerializer();
ser.ext(baseData1, StdSmartPtr{});
EXPECT_FALSE(isPointerContextValid());
@@ -636,11 +684,11 @@ TEST_F(SerializeExtensionStdSmartSharedPtr, WhenOnlyWeakPtrIsSerializedThenPoint
TEST_F(SerializeExtensionStdSmartSharedPtr, WhenOnlyWeakPtrIsDeserializedThenPointerCointextIsInvalid) {
std::shared_ptr<Base> baseData1{new Derived{3, 78}};
auto &ser = createSerializer();
auto& ser = createSerializer();
ser.ext(baseData1, StdSmartPtr{});
std::weak_ptr<Base> baseRes1{};
auto &des = createDeserializer();
auto& des = createDeserializer();
des.ext(baseRes1, StdSmartPtr{});
EXPECT_FALSE(isPointerContextValid());
@@ -653,10 +701,11 @@ TEST_F(SerializeExtensionStdSmartSharedPtr, WhenOnlyWeakPtrIsDeserializedThenPoi
struct TestSharedFromThis : public std::enable_shared_from_this<TestSharedFromThis> {
float x{};
explicit TestSharedFromThis(): std::enable_shared_from_this<TestSharedFromThis>() {}
explicit TestSharedFromThis() : std::enable_shared_from_this<TestSharedFromThis>() {}
template<typename S>
void serialize(S &s) {
void serialize(S& s) {
s.value4b(x);
}
};
@@ -673,18 +722,20 @@ TEST_F(SerializeExtensionStdSmartSharedPtr, EnableSharedFromThis) {
}
struct CustomDeleter {
void operator()(Base*p) {
void operator()(Base* p) {
delete p;
}
};
class SerializeExtensionStdSmartUniquePtr:public SerializeExtensionStdSmartSharedPtr{};
class SerializeExtensionStdSmartUniquePtr : public SerializeExtensionStdSmartSharedPtr {
};
TEST_F(SerializeExtensionStdSmartUniquePtr, WithCustomDeleter) {
std::unique_ptr<Base, CustomDeleter> dataPtr(new Derived{87,7});
std::unique_ptr<Base, CustomDeleter> dataPtr(new Derived{87, 7});
std::unique_ptr<Base, CustomDeleter> resPtr{};
createSerializer().ext(dataPtr, StdSmartPtr{});
createDeserializer().ext(resPtr, StdSmartPtr{});
clearSharedState();
EXPECT_THAT(resPtr->x, Eq(dataPtr->x));
EXPECT_THAT(dynamic_cast<Derived *>(resPtr.get()), ::testing::NotNull());
EXPECT_THAT(dynamic_cast<Derived*>(resPtr.get()), ::testing::NotNull());
}