Fix shared polymorphic poiner like type serialization/deserialization

This commit is contained in:
Mindaugas Vinkelis
2025-10-02 14:20:33 +03:00
parent 7ea1da0d48
commit 66d16516e2
5 changed files with 391 additions and 101 deletions

View File

@@ -104,7 +104,7 @@ struct PtrObserverManager
static void destroyPolymorphic(T& obj,
MemResourceBase*,
PolymorphicHandlerBase&)
const std::shared_ptr<PolymorphicHandlerBase>&)
{
obj = nullptr;
}
@@ -130,47 +130,22 @@ struct NonPtrManager
static void create(T&, MemResourceBase*, size_t) {}
static void createPolymorphic(T&, MemResourceBase*, PolymorphicHandlerBase&)
static void createPolymorphic(T&,
MemResourceBase*,
const std::shared_ptr<PolymorphicHandlerBase>&)
{
}
static void destroy(T&, MemResourceBase*, size_t) {}
static void destroyPolymorphic(T&, MemResourceBase*, PolymorphicHandlerBase&)
static void destroyPolymorphic(T&,
MemResourceBase*,
const std::shared_ptr<PolymorphicHandlerBase>&)
{
}
// LCOV_EXCL_STOP
};
// this class is used by NonPtrManager
struct NoRTTI
{
template<typename TBase>
static size_t get(TBase&)
{
return 0;
}
template<typename TBase>
static constexpr size_t get()
{
return 0;
}
template<typename TBase, typename TDerived>
static constexpr TDerived* cast(TBase* obj)
{
static_assert(!std::is_pointer<TDerived>::value, "");
return dynamic_cast<TDerived*>(obj);
}
template<typename TBase>
static constexpr bool isPolymorphic()
{
return false;
}
};
}
template<typename RTTI>
@@ -179,31 +154,34 @@ using PointerOwnerBase =
PolymorphicContext,
RTTI>;
using PointerOwner = PointerOwnerBase<StandardRTTI>;
using PointerObserver =
template<typename RTTI>
using PointerObserverBase =
pointer_utils::PointerObjectExtensionBase<pointer_details::PtrObserverManager,
PolymorphicContext,
pointer_details::NoRTTI>;
RTTI>;
// inherit from PointerObjectExtensionBase in order to specify
// PointerType::NotNull
class ReferencedByPointer
template<typename RTTI>
class ReferencedByPointerBase
: public pointer_utils::PointerObjectExtensionBase<
pointer_details::NonPtrManager,
PolymorphicContext,
pointer_details::NoRTTI>
RTTI>
{
public:
ReferencedByPointer()
ReferencedByPointerBase()
: pointer_utils::PointerObjectExtensionBase<pointer_details::NonPtrManager,
PolymorphicContext,
pointer_details::NoRTTI>(
PointerType::NotNull)
RTTI>(PointerType::NotNull)
{
}
};
using PointerOwner = PointerOwnerBase<StandardRTTI>;
using PointerObserver = PointerObserverBase<StandardRTTI>;
using ReferencedByPointer = ReferencedByPointerBase<StandardRTTI>;
}
namespace traits {
@@ -219,8 +197,8 @@ struct ExtensionTraits<ext::PointerOwnerBase<RTTI>, T*>
!RTTI::template isPolymorphic<TValue>();
};
template<typename T>
struct ExtensionTraits<ext::PointerObserver, T*>
template<typename T, typename RTTI>
struct ExtensionTraits<ext::PointerObserverBase<RTTI>, T*>
{
// although pointer observer doesn't serialize anything, but we still add
// value overload support to be consistent with pointer owners observer only
@@ -231,8 +209,8 @@ struct ExtensionTraits<ext::PointerObserver, T*>
static constexpr bool SupportLambdaOverload = false;
};
template<typename T>
struct ExtensionTraits<ext::ReferencedByPointer, T>
template<typename T, typename RTTI>
struct ExtensionTraits<ext::ReferencedByPointerBase<RTTI>, T>
{
// allow everything, because it is serialized as regular type, except it also
// creates pointerId that is required by NonOwningPointer to work

View File

@@ -198,10 +198,27 @@ struct SmartPtrOwnerManager
state.obj = std::shared_ptr<TElement>(obj);
}
static void saveToSharedStatePolymorphic(TSharedState& state, T& obj)
{
state.obj = std::shared_ptr<TElement>(obj);
}
static void loadFromSharedState(TSharedState& state, T& obj)
{
// reinterpret_pointer_cast is only since c++17
auto p = reinterpret_cast<TElement*>(state.obj.get());
auto v = state.obj.get();
auto p = reinterpret_cast<TElement*>(v);
obj = std::shared_ptr<TElement>(state.obj, p);
}
static void loadFromSharedStatePolymorphic(TSharedState& state, T& obj)
{
// TODO Fix pointer addresses in case objects are deserialized using
// different bases
// reinterpret_pointer_cast is only since c++17
auto v = state.obj.get();
auto p = reinterpret_cast<TElement*>(v);
obj = std::shared_ptr<TElement>(state.obj, p);
}
};

View File

@@ -120,10 +120,12 @@ struct PLCInfoSerializer : PLCInfo
struct PLCInfoDeserializer : PLCInfo
{
PLCInfoDeserializer(void* ptr,
size_t sharedTypeId_,
PointerOwnershipType ownershipType_,
MemResourceBase* memResource_)
: PLCInfo(ownershipType_)
, ownerPtr{ ptr }
, sharedTypeId{ sharedTypeId_ }
, memResource{ memResource_ }
, observersList{ StdPolyAlloc<std::reference_wrapper<void*>>{
memResource_ } } {};
@@ -157,6 +159,9 @@ struct PLCInfoDeserializer : PLCInfo
}
void* ownerPtr;
// used for polymorphic types in order to identify
// if shared objects can be assigned
size_t sharedTypeId;
MemResourceBase* memResource;
std::vector<std::reference_wrapper<void*>,
StdPolyAlloc<std::reference_wrapper<void*>>>
@@ -254,8 +259,8 @@ public:
PLCInfoDeserializer& getInfoById(size_t id, PointerOwnershipType ptrType)
{
auto res =
_idMap.emplace(id, PLCInfoDeserializer{ nullptr, ptrType, _memResource });
auto res = _idMap.emplace(
id, PLCInfoDeserializer{ nullptr, 0, ptrType, _memResource });
auto& ptrInfo = res.first->second;
if (!res.second)
ptrInfo.update(ptrType);
@@ -352,8 +357,8 @@ public:
if (ptr) {
auto& ctx = ser.template context<
pointer_utils::PointerLinkingContextSerialization>();
auto& ptrInfo =
ctx.getInfoByPtr(getBasePtr(ptr), TPtrManager<T>::getOwnership());
auto& ptrInfo = ctx.getInfoByPtr(getRootPtr(ser, ptr, IsPolymorphic<T>{}),
TPtrManager<T>::getOwnership());
details::writeSize(ser.adapter(), ptrInfo.id);
if (TPtrManager<T>::getOwnership() != PointerOwnershipType::Observer) {
if (!ptrInfo.isSharedProcessed)
@@ -411,7 +416,7 @@ private:
const auto& ctx = des.template context<TPolymorphicContext<RTTI>>();
auto ptr = TPtrManager<TObj>::getPtr(obj);
TPtrManager<TObj>::destroyPolymorphic(
obj, memResource, ctx.getPolymorphicHandler(*ptr));
obj, memResource, ctx.getPolymorphicHandler(ptr));
}
template<typename Des, typename TObj>
@@ -426,16 +431,23 @@ private:
RTTI::template get<typename TPtrManager<TObj>::TElement>());
}
template<typename T>
const void* getBasePtr(const T* ptr) const
template<typename Ser, typename T>
const void* getRootPtr(Ser&, const T* ptr, std::false_type) 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*
return ptr;
}
// same pointer can be accessed through different types with the same base
// e.g. if we have Base class and Derived(Base) we'll get different pointer
// address depending if we access it through Base or Derived
// this function always returns "root" (Base) pointer.
template<typename Ser, typename T>
const void* getRootPtr(Ser& ser, const T* ptr, std::true_type) const
{
const auto& ctx = ser.template context<TPolymorphicContext<RTTI>>();
return ctx.getPolymorphicHandler(ptr)->getRootPtr(ptr);
}
template<typename Ser, typename TPtr, typename Fnc>
void serializeImpl(Ser& ser, TPtr& ptr, Fnc&&, std::true_type) const
{
@@ -506,8 +518,11 @@ private:
std::true_type,
OwnershipType<PointerOwnershipType::SharedOwner>) const
{
if (!ptrInfo.sharedState) {
const auto& ctx = des.template context<TPolymorphicContext<RTTI>>();
const size_t baseTypeId =
RTTI::template get<typename TPtrManager<T>::TElement>();
size_t deserializedTypeId = 0;
if (!ptrInfo.sharedState) {
ctx.deserialize(
des,
TPtrManager<T>::getPtr(obj),
@@ -521,12 +536,46 @@ private:
memResource](const std::shared_ptr<PolymorphicHandlerBase>& handler) {
TPtrManager<T>::destroyPolymorphic(obj, memResource, handler);
});
if (!ptrInfo.sharedState)
TPtrManager<T>::saveToSharedState(
if (!ptrInfo.sharedState) {
TPtrManager<T>::saveToSharedStatePolymorphic(
createAndGetSharedStateObj<T>(ptrInfo), obj);
}
TPtrManager<T>::loadFromSharedState(getSharedStateObj<T>(ptrInfo), obj);
ptrInfo.sharedTypeId =
ctx.getPolymorphicHandler(TPtrManager<T>::getPtr(obj))
->getDerivedTypeId();
// since we just deserialized an object, we can skip checking hierarchy
// chain by assigning baseType id instead of derived type id
deserializedTypeId = baseTypeId;
} else {
deserializedTypeId = ptrInfo.sharedTypeId;
}
if (canAssignToShared(baseTypeId, deserializedTypeId, ctx)) {
TPtrManager<T>::loadFromSharedStatePolymorphic(
getSharedStateObj<T>(ptrInfo), obj);
ptrInfo.processOwner(TPtrManager<T>::getPtr(obj));
} else {
des.adapter().error(ReaderError::InvalidPointer);
}
}
// check if actual deserialized type can be assigned to the base type
// (statically typed)
bool canAssignToShared(size_t baseTypeId,
size_t deserializedTypeId,
const TPolymorphicContext<RTTI>& ctx) const
{
if (baseTypeId == deserializedTypeId)
return true;
auto bases = ctx.getDirectBases(deserializedTypeId);
if (bases) {
for (auto typeId : *bases) {
if (canAssignToShared(baseTypeId, typeId, ctx)) {
return true;
}
}
}
return false;
}
template<typename Des, typename T, typename Fnc>
@@ -538,6 +587,8 @@ private:
std::false_type,
OwnershipType<PointerOwnershipType::SharedOwner>) const
{
const size_t baseTypeId =
RTTI::template get<typename TPtrManager<T>::TElement>();
if (!ptrInfo.sharedState) {
auto ptr = TPtrManager<T>::getPtr(obj);
if (ptr) {
@@ -552,9 +603,15 @@ private:
ptr = TPtrManager<T>::getPtr(obj);
}
fnc(des, *ptr);
ptrInfo.sharedTypeId =
RTTI::template get<typename TPtrManager<T>::TElement>();
}
if (baseTypeId == ptrInfo.sharedTypeId) {
TPtrManager<T>::loadFromSharedState(getSharedStateObj<T>(ptrInfo), obj);
ptrInfo.processOwner(TPtrManager<T>::getPtr(obj));
} else {
des.adapter().error(ReaderError::InvalidPointer);
}
}
template<typename Des, typename T, typename Fnc, typename isPolymorph>

View File

@@ -74,10 +74,18 @@ public:
virtual void process(void* ser, void* obj) const = 0;
virtual void* getRootPtr(const void* obj) const = 0;
virtual size_t getDerivedTypeId() const = 0;
virtual ~PolymorphicHandlerBase() = default;
};
template<typename RTTI, typename TSerializer, typename TBase, typename TDerived>
template<typename RTTI,
typename TSerializer,
typename TRoot,
typename TBase,
typename TDerived>
class PolymorphicHandler : public PolymorphicHandlerBase
{
public:
@@ -97,6 +105,17 @@ public:
static_cast<TSerializer*>(ser)->object(*fromBase(obj));
}
void* getRootPtr(const void* obj) const final
{
return RTTI::template cast<TBase, TRoot>(
static_cast<TBase*>(const_cast<void*>(obj)));
}
size_t getDerivedTypeId() const final
{
return RTTI::template get<TDerived>();
}
private:
TDerived* fromBase(void* obj) const
{
@@ -109,6 +128,35 @@ private:
}
};
// Even though we don't serialize/deserialize abstract classes
// object might still be accessed through abstract class, hence we need this
// for type information
template<typename RTTI, typename TRoot, typename TBase, typename TDerived>
class AbstractPolymorphicHandler : public PolymorphicHandlerBase
{
public:
void* create(const pointer_utils::PolyAllocWithTypeId& alloc) const
{
assert(false);
return nullptr;
}
void destroy(const pointer_utils::PolyAllocWithTypeId& alloc, void* ptr) const
{
assert(false);
};
void process(void* ser, void* obj) const { assert(false); }
void* getRootPtr(const void* obj) const
{
assert(false);
return nullptr;
}
size_t getDerivedTypeId() const { return RTTI::template get<TDerived>(); };
};
template<typename RTTI>
class PolymorphicContext
{
@@ -137,47 +185,54 @@ private:
template<typename TSerializer,
template<typename>
class THierarchy,
typename TRoot,
typename TBase,
typename TDerived>
void add()
void add(size_t depth)
{
addToMap<TSerializer, TBase, TDerived>(std::is_abstract<TDerived>{});
addChilds<TSerializer, THierarchy, TBase, TDerived>(
typename THierarchy<TDerived>::Childs{});
addToMap<TSerializer, TRoot, TBase, TDerived>(depth == 1,
std::is_abstract<TDerived>{});
addChilds<TSerializer, THierarchy, TRoot, TBase, TDerived>(
depth + 1, typename THierarchy<TDerived>::Childs{});
}
template<typename TSerializer,
template<typename>
class THierarchy,
typename TRoot,
typename TBase,
typename TDerived,
typename T1,
typename... Tn>
void addChilds(PolymorphicClassesList<T1, Tn...>)
void addChilds(size_t depth, PolymorphicClassesList<T1, Tn...>)
{
static_assert(std::is_base_of<TDerived, T1>::value,
"PolymorphicBaseClass<TBase> must derive a list of derived "
"classes from TBase.");
add<TSerializer, THierarchy, TBase, T1>();
addChilds<TSerializer, THierarchy, TBase, TDerived>(
PolymorphicClassesList<Tn...>{});
// iterate through derived class hierarchy as well
add<TSerializer, THierarchy, T1, T1>();
add<TSerializer, THierarchy, TRoot, TBase, T1>(depth);
addChilds<TSerializer, THierarchy, TRoot, TBase, TDerived>(
depth, PolymorphicClassesList<Tn...>{});
add<TSerializer, THierarchy, TRoot, T1, T1>(0);
}
template<typename TSerializer,
template<typename>
class THierarchy,
typename TRoot,
typename TBase,
typename TDerived>
void addChilds(PolymorphicClassesList<>)
void addChilds(size_t, PolymorphicClassesList<>)
{
}
template<typename TSerializer, typename TBase, typename TDerived>
void addToMap(std::false_type)
template<typename TSerializer,
typename TRoot,
typename TBase,
typename TDerived>
void addToMap(bool directBase, std::false_type)
{
using THandler = PolymorphicHandler<RTTI, TSerializer, TBase, TDerived>;
using THandler =
PolymorphicHandler<RTTI, TSerializer, TRoot, TBase, TDerived>;
BaseToDerivedKey key{ RTTI::template get<TBase>(),
RTTI::template get<TDerived>() };
pointer_utils::StdPolyAlloc<THandler> alloc{ _memResource };
@@ -201,12 +256,51 @@ private:
}
it->second.push_back(key.derivedHash);
}
if (directBase) {
auto it = _derivedToBaseArray.find(key.derivedHash);
if (it == _derivedToBaseArray.end()) {
it = _derivedToBaseArray
.emplace(std::piecewise_construct,
std::forward_as_tuple(key.derivedHash),
std::forward_as_tuple(
pointer_utils::StdPolyAlloc<size_t>{ _memResource }))
.first;
}
it->second.push_back(key.baseHash);
}
}
template<typename TSerializer, typename TBase, typename TDerived>
void addToMap(std::true_type)
template<typename TSerializer,
typename TRoot,
typename TBase,
typename TDerived>
void addToMap(bool directBase, std::true_type)
{
// cannot add abstract class
using THandler = AbstractPolymorphicHandler<RTTI, TRoot, TBase, TDerived>;
BaseToDerivedKey key{ RTTI::template get<TBase>(),
RTTI::template get<TDerived>() };
pointer_utils::StdPolyAlloc<THandler> alloc{ _memResource };
auto ptr = alloc.allocate(1);
std::shared_ptr<THandler> handler(
new (ptr) THandler{},
[alloc](THandler* data) mutable {
data->~THandler();
alloc.deallocate(data, 1);
},
alloc);
_baseToDerivedMap.emplace(key, std::move(handler)).second;
if (directBase) {
auto it = _derivedToBaseArray.find(key.derivedHash);
if (it == _derivedToBaseArray.end()) {
it = _derivedToBaseArray
.emplace(std::piecewise_construct,
std::forward_as_tuple(key.derivedHash),
std::forward_as_tuple(
pointer_utils::StdPolyAlloc<size_t>{ _memResource }))
.first;
}
it->second.push_back(key.baseHash);
}
}
MemResourceBase* _memResource;
@@ -234,6 +328,17 @@ private:
std::vector<size_t, pointer_utils::StdPolyAlloc<size_t>>>>>
_baseToDerivedArray;
// Used to iterate through hierarchy chain from most derived to the base(s)
std::unordered_map<
size_t,
std::vector<size_t, pointer_utils::StdPolyAlloc<size_t>>,
std::hash<size_t>,
std::equal_to<size_t>,
pointer_utils::StdPolyAlloc<
std::pair<const size_t,
std::vector<size_t, pointer_utils::StdPolyAlloc<size_t>>>>>
_derivedToBaseArray;
public:
explicit PolymorphicContext(MemResourceBase* memResource = nullptr)
: _memResource{ memResource }
@@ -244,6 +349,11 @@ public:
std::pair<const size_t,
std::vector<size_t, pointer_utils::StdPolyAlloc<size_t>>>>{
memResource } }
, _derivedToBaseArray{ pointer_utils::StdPolyAlloc<
std::pair<const size_t,
std::vector<size_t, pointer_utils::StdPolyAlloc<size_t>>>>{
memResource } }
{
}
@@ -269,7 +379,7 @@ public:
typename... Tn>
void registerBasesList(PolymorphicClassesList<T1, Tn...>)
{
add<TSerializer, THierarchy, T1, T1>();
add<TSerializer, THierarchy, T1, T1, T1>(0);
registerBasesList<TSerializer, THierarchy>(PolymorphicClassesList<Tn...>{});
}
@@ -278,18 +388,6 @@ public:
{
}
// optional method, in case you want to construct base class hierarchy your
// self
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");
addToMap<TSerializer, TBase, TDerived>(std::false_type{});
}
template<typename Serializer, typename TBase>
void serialize(Serializer& ser, TBase& obj) const
{
@@ -338,7 +436,7 @@ public:
// if object is null or different type, create new and assign it
if (obj == nullptr || RTTI::template get<TBase>(*obj) != derivedHash) {
if (obj) {
destroyFnc(getPolymorphicHandler(*obj));
destroyFnc(getPolymorphicHandler(obj));
}
obj = createFnc(handler);
}
@@ -349,12 +447,32 @@ public:
template<typename TBase>
const std::shared_ptr<PolymorphicHandlerBase>& getPolymorphicHandler(
TBase& obj) const
TBase* obj) const
{
auto deleteHandlerIt = _baseToDerivedMap.find(BaseToDerivedKey{
RTTI::template get<TBase>(), RTTI::template get<TBase>(obj) });
assert(deleteHandlerIt != _baseToDerivedMap.end());
return deleteHandlerIt->second;
auto it = _baseToDerivedMap.find(BaseToDerivedKey{
RTTI::template get<TBase>(), RTTI::template get<TBase>(*obj) });
assert(it != _baseToDerivedMap.end());
return it->second;
}
template<typename TBase>
const std::shared_ptr<PolymorphicHandlerBase>& getPolymorphicHandler() const
{
auto it = _baseToDerivedMap.find(BaseToDerivedKey{
RTTI::template get<TBase>(), RTTI::template get<TBase>() });
assert(it != _baseToDerivedMap.end());
return it->second;
}
const std::vector<size_t, pointer_utils::StdPolyAlloc<size_t>>*
getDirectBases(size_t derivedTypeId) const
{
auto it = _derivedToBaseArray.find(derivedTypeId);
if (it != _derivedToBaseArray.end()) {
return &it->second;
} else {
return nullptr;
}
}
};

View File

@@ -23,6 +23,8 @@
#include <bitsery/ext/inheritance.h>
#include <bitsery/ext/pointer.h>
#include <bitsery/ext/std_smart_ptr.h>
#include <bitsery/traits/string.h>
#include <bitsery/traits/vector.h>
#include "serialization_test_utils.h"
#include <gmock/gmock.h>
@@ -77,6 +79,27 @@ serialize(S& s, Derived& o)
s.value1b(o.y);
}
struct DerivedSibling : virtual Base
{
uint32_t y{};
DerivedSibling() = default;
DerivedSibling(uint8_t x_, uint32_t y_)
{
x = x_;
y = y_;
}
};
template<typename S>
void
serialize(S& s, DerivedSibling& o)
{
s.ext(o, VirtualBaseClass<Base>{});
s.value4b(o.y);
}
struct MoreDerived : Derived
{
uint8_t z{};
@@ -741,6 +764,103 @@ TEST_F(SerializeExtensionStdSmartSharedPtr,
EXPECT_THAT(baseRes1.use_count(), Eq(0));
}
struct MightMatchVecLayout
{
size_t begin;
size_t end;
};
template<typename S>
void
serialize(S& s, MightMatchVecLayout& o)
{
s.template value<sizeof(size_t)>(o.begin);
s.template value<sizeof(size_t)>(o.end);
}
TEST_F(SerializeExtensionStdSmartSharedPtr,
NonPolymorphicObservedPointerMustMatchActuallyDeserializedObjectType)
{
std::shared_ptr<std::vector<uint8_t>> baseData1{ new std::vector<uint8_t>{
'a', 'b', 'c' } };
std::shared_ptr<MightMatchVecLayout> baseData2{ new MightMatchVecLayout{
1, 2 } };
auto& ser = createSerializer();
ser.ext(baseData1, StdSmartPtr{}, [](auto& ser, std::vector<uint8_t>& o) {
ser.container1b(o, 100);
});
ser.ext(baseData2, StdSmartPtr{});
// hack a buffer, so that during deserialization we would point to already
// deserialized object
sctx.buf[5] = 0x01;
std::shared_ptr<std::vector<uint8_t>> baseRes1{};
std::shared_ptr<MightMatchVecLayout> baseRes2{};
auto& des = createDeserializer();
des.ext(baseRes1, StdSmartPtr{}, [](auto& ser, std::vector<uint8_t>& o) {
ser.container1b(o, 100);
});
// if we would blindly trust the input (that the object is already
// deserialized), we would end up
// using memory of that object. In this case, we will get internal
// representation of std::vector.
// If this object is available to attacker, he'll be able to see leaked
// address of that object,
// allowing him to bypass ASLR.
des.ext(baseRes2, StdSmartPtr{});
EXPECT_THAT(baseRes2.get(), ::testing::IsNull());
auto err = des.adapter().error();
EXPECT_THAT(err, Eq(bitsery::ReaderError::InvalidPointer));
EXPECT_TRUE(isPointerContextValid());
}
TEST_F(
SerializeExtensionStdSmartSharedPtr,
PolymorphicObservedPointerMustBeInInheritanceChainOfActuallyDeserializedObjectType1)
{
std::shared_ptr<Base> baseData1{ new MoreDerived{ 3, 7, 10 } };
std::shared_ptr<Base> baseData2{};
baseData2 = baseData1;
auto& ser = createSerializer();
ser.ext(baseData1, StdSmartPtr{});
ser.ext(baseData2, StdSmartPtr{});
std::shared_ptr<Base> baseRes1{};
std::shared_ptr<Derived> baseRes2{};
auto& des = createDeserializer();
des.ext(baseRes1, StdSmartPtr{});
des.ext(baseRes2, StdSmartPtr{});
EXPECT_THAT(baseRes2.get(), ::testing::NotNull());
auto err = des.adapter().error();
EXPECT_THAT(err, Eq(bitsery::ReaderError::NoError));
EXPECT_TRUE(isPointerContextValid());
}
TEST_F(
SerializeExtensionStdSmartSharedPtr,
PolymorphicObservedPointerMustBeInInheritanceChainOfActuallyDeserializedObjectType2)
{
std::shared_ptr<Base> baseData1{ new MoreDerived{ 3, 7, 10 } };
std::shared_ptr<Base> baseData2{};
baseData2 = baseData1;
auto& ser = createSerializer();
ser.ext(baseData1, StdSmartPtr{});
ser.ext(baseData2, StdSmartPtr{});
std::shared_ptr<Base> baseRes1{};
std::shared_ptr<DerivedSibling> baseRes2{};
auto& des = createDeserializer();
des.ext(baseRes1, StdSmartPtr{});
des.ext(baseRes2, StdSmartPtr{});
EXPECT_THAT(baseRes2.get(), ::testing::IsNull());
auto err = des.adapter().error();
EXPECT_THAT(err, Eq(bitsery::ReaderError::InvalidPointer));
EXPECT_TRUE(isPointerContextValid());
}
struct TestSharedFromThis
: public std::enable_shared_from_this<TestSharedFromThis>
{