cast from polymorphic owner pointer-like type to observer during assignment

This commit is contained in:
Mindaugas Vinkelis
2025-10-08 20:21:05 +03:00
parent ff841d63f6
commit 5e7ecede9b
4 changed files with 53 additions and 103 deletions

View File

@@ -147,6 +147,7 @@ struct SmartPtrOwnerManager
[alloc, typeId](TElement* data) { alloc.deleteObject(data, typeId); }, [alloc, typeId](TElement* data) { alloc.deleteObject(data, typeId); },
pointer_utils::StdPolyAlloc<TElement>(memResource)); pointer_utils::StdPolyAlloc<TElement>(memResource));
state.obj = obj; state.obj = obj;
state.typeId = typeId;
} }
static void createSharedPolymorphic( static void createSharedPolymorphic(
@@ -162,6 +163,7 @@ struct SmartPtrOwnerManager
[alloc, handler](TElement* data) { handler->destroy(alloc, data); }, [alloc, handler](TElement* data) { handler->destroy(alloc, data); },
pointer_utils::StdPolyAlloc<TElement>(memResource)); pointer_utils::StdPolyAlloc<TElement>(memResource));
state.obj = obj; state.obj = obj;
state.typeId = handler->getDerivedTypeId();
} }
static void createShared(TSharedState& state, static void createShared(TSharedState& state,
@@ -176,6 +178,7 @@ struct SmartPtrOwnerManager
pointer_utils::StdPolyAlloc<TElement>(memResource)); pointer_utils::StdPolyAlloc<TElement>(memResource));
obj = res; obj = res;
state.obj = res; state.obj = res;
state.typeId = typeId;
} }
static void createSharedPolymorphic( static void createSharedPolymorphic(
@@ -191,6 +194,7 @@ struct SmartPtrOwnerManager
pointer_utils::StdPolyAlloc<TElement>(memResource)); pointer_utils::StdPolyAlloc<TElement>(memResource));
obj = res; obj = res;
state.obj = res; state.obj = res;
state.typeId = handler->getDerivedTypeId();
} }
static void saveToSharedState(TSharedState& state, T& obj) static void saveToSharedState(TSharedState& state, T& obj)
@@ -205,20 +209,17 @@ struct SmartPtrOwnerManager
static void loadFromSharedState(TSharedState& state, T& obj) static void loadFromSharedState(TSharedState& state, T& obj)
{ {
// reinterpret_pointer_cast is only since c++17
auto v = state.obj.get(); auto v = state.obj.get();
auto p = reinterpret_cast<TElement*>(v); auto p = static_cast<TElement*>(v);
obj = std::shared_ptr<TElement>(state.obj, p); obj = std::shared_ptr<TElement>(state.obj, p);
} }
static void loadFromSharedStatePolymorphic(TSharedState& state, T& obj) static void loadFromSharedStatePolymorphic(TSharedState& state,
T& obj,
const PolymorphicHandlerBase&)
{ {
// 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 v = state.obj.get();
auto p = reinterpret_cast<TElement*>(v); auto p = static_cast<TElement*>(v);
obj = std::shared_ptr<TElement>(state.obj, p); obj = std::shared_ptr<TElement>(state.obj, p);
} }
}; };

View File

@@ -56,6 +56,7 @@ namespace pointer_utils {
// this class is used to store context for shared ptr owners // this class is used to store context for shared ptr owners
struct PointerSharedStateBase struct PointerSharedStateBase
{ {
size_t typeId{};
virtual ~PointerSharedStateBase() = default; virtual ~PointerSharedStateBase() = default;
}; };
@@ -541,10 +542,10 @@ private:
} else { } else {
deserializedTypeId = ptrInfo.ownerTypeId; deserializedTypeId = ptrInfo.ownerTypeId;
} }
if (auto hndl =
if (canAssignToBase(baseTypeId, deserializedTypeId, ctx)) { ctx.getPolymorphicHandler(baseTypeId, ptrInfo.sharedState->typeId)) {
TPtrManager<T>::loadFromSharedStatePolymorphic( TPtrManager<T>::loadFromSharedStatePolymorphic(
getSharedStateObj<T>(ptrInfo), obj); getSharedStateObj<T>(ptrInfo), obj, **hndl);
processObserverListPolymorphic(des, ptrInfo, ctx); processObserverListPolymorphic(des, ptrInfo, ctx);
} else { } else {
des.adapter().error(ReaderError::InvalidPointer); des.adapter().error(ReaderError::InvalidPointer);
@@ -643,9 +644,9 @@ private:
RTTI::template get<typename TPtrManager<T>::TElement>(); RTTI::template get<typename TPtrManager<T>::TElement>();
void*(&ptr) = reinterpret_cast<void*&>(TPtrManager<T>::getPtrRef(obj)); void*(&ptr) = reinterpret_cast<void*&>(TPtrManager<T>::getPtrRef(obj));
if (ptrInfo.ownerPtr) { if (ptrInfo.ownerPtr) {
if (canAssignToBase(baseTypeId, ptrInfo.ownerTypeId, ctx)) { if (auto hndl =
// TODO cast from one ptr to another ctx.getPolymorphicHandler(baseTypeId, ptrInfo.ownerTypeId)) {
ptr = ptrInfo.ownerPtr; ptr = hndl->get()->fromDerivedToBasePtr(ptrInfo.ownerPtr);
} else { } else {
des.adapter().error(ReaderError::InvalidPointer); des.adapter().error(ReaderError::InvalidPointer);
} }
@@ -654,25 +655,6 @@ private:
} }
} }
// check if actual deserialized type can be assigned to the base type
// (statically typed)
bool canAssignToBase(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 (canAssignToBase(baseTypeId, typeId, ctx)) {
return true;
}
}
}
return false;
}
template<typename Des> template<typename Des>
void processObserverList(Des& des, PLCInfoDeserializer& ptrInfo) const void processObserverList(Des& des, PLCInfoDeserializer& ptrInfo) const
{ {
@@ -696,9 +678,9 @@ private:
{ {
assert(ptrInfo.ownershipType != PointerOwnershipType::Observer); assert(ptrInfo.ownershipType != PointerOwnershipType::Observer);
for (auto& o : ptrInfo.observersList) { for (auto& o : ptrInfo.observersList) {
if (canAssignToBase(o.baseTypeId, ptrInfo.ownerTypeId, ctx)) { if (auto hndl =
// TODO cast from one ptr to another ctx.getPolymorphicHandler(o.baseTypeId, ptrInfo.ownerTypeId)) {
o.obj.get() = ptrInfo.ownerPtr; o.obj.get() = hndl->get()->fromDerivedToBasePtr(ptrInfo.ownerPtr);
} else { } else {
des.adapter().error(ReaderError::InvalidPointer); des.adapter().error(ReaderError::InvalidPointer);
} }

View File

@@ -76,6 +76,8 @@ public:
virtual void* getRootPtr(const void* obj) const = 0; virtual void* getRootPtr(const void* obj) const = 0;
virtual void* fromDerivedToBasePtr(void* obj) const = 0;
virtual size_t getDerivedTypeId() const = 0; virtual size_t getDerivedTypeId() const = 0;
virtual ~PolymorphicHandlerBase() = default; virtual ~PolymorphicHandlerBase() = default;
@@ -111,6 +113,8 @@ public:
static_cast<TBase*>(const_cast<void*>(obj))); static_cast<TBase*>(const_cast<void*>(obj)));
} }
void* fromDerivedToBasePtr(void* obj) const final { return toBase(obj); }
size_t getDerivedTypeId() const final size_t getDerivedTypeId() const final
{ {
return RTTI::template get<TDerived>(); return RTTI::template get<TDerived>();
@@ -154,6 +158,12 @@ public:
return nullptr; return nullptr;
} }
void* fromDerivedToBasePtr(void*) const final
{
assert(false);
return nullptr;
}
size_t getDerivedTypeId() const { return RTTI::template get<TDerived>(); }; size_t getDerivedTypeId() const { return RTTI::template get<TDerived>(); };
}; };
@@ -188,12 +198,11 @@ private:
typename TRoot, typename TRoot,
typename TBase, typename TBase,
typename TDerived> typename TDerived>
void add(size_t depth) void add()
{ {
addToMap<TSerializer, TRoot, TBase, TDerived>(depth == 1, addToMap<TSerializer, TRoot, TBase, TDerived>(std::is_abstract<TDerived>{});
std::is_abstract<TDerived>{});
addChilds<TSerializer, THierarchy, TRoot, TBase, TDerived>( addChilds<TSerializer, THierarchy, TRoot, TBase, TDerived>(
depth + 1, typename THierarchy<TDerived>::Childs{}); typename THierarchy<TDerived>::Childs{});
} }
template<typename TSerializer, template<typename TSerializer,
@@ -204,15 +213,15 @@ private:
typename TDerived, typename TDerived,
typename T1, typename T1,
typename... Tn> typename... Tn>
void addChilds(size_t depth, PolymorphicClassesList<T1, Tn...>) void addChilds(PolymorphicClassesList<T1, Tn...>)
{ {
static_assert(std::is_base_of<TDerived, T1>::value, static_assert(std::is_base_of<TDerived, T1>::value,
"PolymorphicBaseClass<TBase> must derive a list of derived " "PolymorphicBaseClass<TBase> must derive a list of derived "
"classes from TBase."); "classes from TBase.");
add<TSerializer, THierarchy, TRoot, TBase, T1>(depth); add<TSerializer, THierarchy, TRoot, TBase, T1>();
addChilds<TSerializer, THierarchy, TRoot, TBase, TDerived>( addChilds<TSerializer, THierarchy, TRoot, TBase, TDerived>(
depth, PolymorphicClassesList<Tn...>{}); PolymorphicClassesList<Tn...>{});
add<TSerializer, THierarchy, TRoot, T1, T1>(0); add<TSerializer, THierarchy, TRoot, T1, T1>();
} }
template<typename TSerializer, template<typename TSerializer,
@@ -221,7 +230,7 @@ private:
typename TRoot, typename TRoot,
typename TBase, typename TBase,
typename TDerived> typename TDerived>
void addChilds(size_t, PolymorphicClassesList<>) void addChilds(PolymorphicClassesList<>)
{ {
} }
@@ -229,7 +238,7 @@ private:
typename TRoot, typename TRoot,
typename TBase, typename TBase,
typename TDerived> typename TDerived>
void addToMap(bool directBase, std::false_type) void addToMap(std::false_type)
{ {
using THandler = using THandler =
PolymorphicHandler<RTTI, TSerializer, TRoot, TBase, TDerived>; PolymorphicHandler<RTTI, TSerializer, TRoot, TBase, TDerived>;
@@ -256,25 +265,13 @@ private:
} }
it->second.push_back(key.derivedHash); 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, template<typename TSerializer,
typename TRoot, typename TRoot,
typename TBase, typename TBase,
typename TDerived> typename TDerived>
void addToMap(bool directBase, std::true_type) void addToMap(std::true_type)
{ {
using THandler = AbstractPolymorphicHandler<RTTI, TRoot, TBase, TDerived>; using THandler = AbstractPolymorphicHandler<RTTI, TRoot, TBase, TDerived>;
BaseToDerivedKey key{ RTTI::template get<TBase>(), BaseToDerivedKey key{ RTTI::template get<TBase>(),
@@ -288,19 +285,7 @@ private:
alloc.deallocate(data, 1); alloc.deallocate(data, 1);
}, },
alloc); alloc);
_baseToDerivedMap.emplace(key, std::move(handler)).second; _baseToDerivedMap.emplace(key, std::move(handler));
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; MemResourceBase* _memResource;
@@ -328,17 +313,6 @@ private:
std::vector<size_t, pointer_utils::StdPolyAlloc<size_t>>>>> std::vector<size_t, pointer_utils::StdPolyAlloc<size_t>>>>>
_baseToDerivedArray; _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: public:
explicit PolymorphicContext(MemResourceBase* memResource = nullptr) explicit PolymorphicContext(MemResourceBase* memResource = nullptr)
: _memResource{ memResource } : _memResource{ memResource }
@@ -349,11 +323,6 @@ public:
std::pair<const size_t, std::pair<const size_t,
std::vector<size_t, pointer_utils::StdPolyAlloc<size_t>>>>{ std::vector<size_t, pointer_utils::StdPolyAlloc<size_t>>>>{
memResource } } memResource } }
, _derivedToBaseArray{ pointer_utils::StdPolyAlloc<
std::pair<const size_t,
std::vector<size_t, pointer_utils::StdPolyAlloc<size_t>>>>{
memResource } }
{ {
} }
@@ -379,7 +348,7 @@ public:
typename... Tn> typename... Tn>
void registerBasesList(PolymorphicClassesList<T1, Tn...>) void registerBasesList(PolymorphicClassesList<T1, Tn...>)
{ {
add<TSerializer, THierarchy, T1, T1, T1>(0); add<TSerializer, THierarchy, T1, T1, T1>();
registerBasesList<TSerializer, THierarchy>(PolymorphicClassesList<Tn...>{}); registerBasesList<TSerializer, THierarchy>(PolymorphicClassesList<Tn...>{});
} }
@@ -464,15 +433,16 @@ public:
return it->second; return it->second;
} }
const std::vector<size_t, pointer_utils::StdPolyAlloc<size_t>>* const std::shared_ptr<PolymorphicHandlerBase>* getPolymorphicHandler(
getDirectBases(size_t derivedTypeId) const size_t baseTypeId,
size_t derivedTypeId) const
{ {
auto it = _derivedToBaseArray.find(derivedTypeId); auto it =
if (it != _derivedToBaseArray.end()) { _baseToDerivedMap.find(BaseToDerivedKey{ baseTypeId, derivedTypeId });
return &it->second; if (it == _baseToDerivedMap.end()) {
} else {
return nullptr; return nullptr;
} }
return &it->second;
} }
}; };

View File

@@ -149,12 +149,10 @@ struct PolymorphicBaseClass<Base>
: PolymorphicDerivedClasses<Derived1, Derived2> : PolymorphicDerivedClasses<Derived1, Derived2>
{}; {};
// this is commented on purpose, to test scenario when base class is registered template<>
// (Base) but using instance of Derived1 which is not registered as base struct PolymorphicBaseClass<Derived1>
// template<> : PolymorphicDerivedClasses<MultipleVirtualInheritance>
// struct PolymorphicBaseClass<Derived1> : {};
// PolymorphicDerivedClasses<MultipleVirtualInheritance> {
// };
template<> template<>
struct PolymorphicBaseClass<Derived2> struct PolymorphicBaseClass<Derived2>
@@ -410,8 +408,7 @@ TEST_F(SerializeExtensionPointerPolymorphicTypes,
Eq(bitsery::ReaderError::InvalidPointer)); Eq(bitsery::ReaderError::InvalidPointer));
} }
TEST_F(SerializeExtensionPointerPolymorphicTypes, TEST_F(SerializeExtensionPointerPolymorphicTypes, OwnerIsCastObserverType)
OwnerIsStaticallyCastToObserverType)
{ {
MultipleVirtualInheritance md{ 1, 2, 3, 4 }; MultipleVirtualInheritance md{ 1, 2, 3, 4 };