mirror of
https://github.com/fraillt/bitsery.git
synced 2026-10-18 14:56:01 +00:00
added custom allocator support for pointer like objects.
This commit is contained in:
committed by
Mindaugas Vinkelis
parent
03f2c3c8b5
commit
57dd028b7a
@@ -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, "");
|
||||
|
||||
Reference in New Issue
Block a user