mirror of
https://github.com/fraillt/bitsery.git
synced 2026-08-18 17:20:00 +00:00
added custom allocator support for pointer like objects.
This commit is contained in:
committed by
Mindaugas Vinkelis
parent
03f2c3c8b5
commit
57dd028b7a
@@ -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
|
||||
|
||||
@@ -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
40
doc/design/pointers.md
Normal 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.
|
||||
@@ -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{};
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@@ -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) {}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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>;
|
||||
|
||||
95
include/bitsery/ext/utils/memory_allocator.h
Normal file
95
include/bitsery/ext/utils/memory_allocator.h
Normal 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
|
||||
@@ -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();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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, "");
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
364
tests/serialization_ext_pointer_with_allocator.cpp
Normal file
364
tests/serialization_ext_pointer_with_allocator.cpp
Normal 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));
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user