added custom allocator support for pointer like objects.

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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