6 Commits

Author SHA1 Message Date
Mindaugas Vinkelis
9bebfd4911 prepare for 5.2.5 2025-10-08 21:21:38 +03:00
Mindaugas Vinkelis
5e7ecede9b cast from polymorphic owner pointer-like type to observer during assignment 2025-10-08 20:36:15 +03:00
Mindaugas Vinkelis
ff841d63f6 check type information before assigning to observer pointer 2025-10-08 13:13:20 +03:00
Mindaugas Vinkelis
66d16516e2 Fix shared polymorphic poiner like type serialization/deserialization 2025-10-02 14:20:33 +03:00
Mindaugas Vinkelis
7ea1da0d48 clang-format using ./format.sh 2025-09-09 17:38:23 +03:00
Jules
8bda82576e Fix spelling error of "likely" 2025-02-09 15:21:02 +02:00
30 changed files with 599 additions and 383 deletions

8
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,8 @@
---
github: fraillt
buy_me_a_coffee: fraillt
custom:
- "https://www.paypal.com/paypalme/fraillt"
- "https://explorer.solana.com/address/5uHU32nBuniRxg6RZu4tsLWrXGFFz4pwMGHGuCLmkGJQ"
- "https://etherscan.io/address/0xe51cb417d1BFcd3EE4cfad9fa11b05631823AADb"
- "https://polygonscan.com/address/0xe51cb417d1BFcd3EE4cfad9fa11b05631823AADb"

View File

@@ -1,3 +1,15 @@
# [5.2.5](https://github.com/fraillt/bitsery/compare/v5.2.4...v5.2.5) (2025-10-09)
### Bug fixes
* fix security issue during deserialization where a crafted payload could cause a shared pointer to be assigned to a different type. More information is [here](https://gist.github.com/TrebledJ/750abc64a826f19dd2d6774724629b71). (huge thanks to [Johnathan](https://github.com/TrebledJ))
* fix serialization of shared polymorphic pointer-like types by correctly identifying same object (e.g. the same object serialized through `Base` or `Derived` would otherwise have different pointer addresses).
* fix polymorphic type assignment to "observer" by adjusting pointer address.
* fix spelling of C++ "likely" attribute. #121 (thanks to [Jules](https://github.com/jules-ai))
### Other notes
* format code that was left unformatted in the previous version.
* remove broken patch for GCC 4.8.2 (CentOS 7).
# [5.2.4](https://github.com/fraillt/bitsery/compare/v5.2.3...v5.2.4) (2024-07-30) # [5.2.4](https://github.com/fraillt/bitsery/compare/v5.2.3...v5.2.4) (2024-07-30)
### Improvements ### Improvements

View File

@@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.25) cmake_minimum_required(VERSION 3.25)
project(bitsery project(bitsery
LANGUAGES CXX LANGUAGES CXX
VERSION 5.2.4) VERSION 5.2.5)
#======== build options =================================== #======== build options ===================================
option(BITSERY_BUILD_EXAMPLES "Build examples" OFF) option(BITSERY_BUILD_EXAMPLES "Build examples" OFF)

View File

@@ -102,9 +102,13 @@ Works with C++11 compiler, no additional dependencies, include `<bitsery/bitsery
Library is tested on all major compilers on Windows, Linux and macOS. Library is tested on all major compilers on Windows, Linux and macOS.
There is a patch that allows using bitsery with non-fully compatible C++11 compilers.
* CentOS 7 with gcc 4.8.2.
## License ## License
**bitsery** is licensed under the [MIT license](LICENSE). **bitsery** is licensed under the [MIT license](LICENSE).
## 💖 Sponsor Me?
If you find this project useful or interesting, or just want to say thanks, you can buy me a coffee!
Your support keeps me motivated to maintaining and improving this project.
[**Thank you!**](https://github.com/sponsors/fraillt)

View File

@@ -95,14 +95,12 @@ namespace bitsery {
// and Derived2 member fnc we need explicitly select which function to use // and Derived2 member fnc we need explicitly select which function to use
template<> template<>
struct SelectSerializeFnc<Derived2> : UseMemberFnc struct SelectSerializeFnc<Derived2> : UseMemberFnc
{ {};
};
// multiple inheritance has non-member serialize function defined // multiple inheritance has non-member serialize function defined
template<> template<>
struct SelectSerializeFnc<MultipleInheritance> : UseNonMemberFnc struct SelectSerializeFnc<MultipleInheritance> : UseNonMemberFnc
{ {};
};
} }
// some helper types // some helper types

View File

@@ -191,14 +191,12 @@ namespace ext {
template<> template<>
struct PolymorphicBaseClass<Shape> struct PolymorphicBaseClass<Shape>
: PolymorphicDerivedClasses<Circle, Rectangle> : PolymorphicDerivedClasses<Circle, Rectangle>
{ {};
};
template<> template<>
struct PolymorphicBaseClass<Rectangle> struct PolymorphicBaseClass<Rectangle>
: PolymorphicDerivedClasses<RoundedRectangle> : PolymorphicDerivedClasses<RoundedRectangle>
{ {};
};
} }
} }

View File

@@ -25,7 +25,7 @@
#define BITSERY_MAJOR_VERSION 5 #define BITSERY_MAJOR_VERSION 5
#define BITSERY_MINOR_VERSION 2 #define BITSERY_MINOR_VERSION 2
#define BITSERY_PATCH_VERSION 4 #define BITSERY_PATCH_VERSION 5
#define BITSERY_QUOTE_MACRO(name) #name #define BITSERY_QUOTE_MACRO(name) #name
#define BITSERY_BUILD_VERSION_STR(major, minor, patch) \ #define BITSERY_BUILD_VERSION_STR(major, minor, patch) \
@@ -66,7 +66,7 @@
#endif #endif
#if __has_cpp_attribute(likely) #if __has_cpp_attribute(likely)
#define BITSERY_LIKELY BITSERY_ATTRIBUTE(likey) #define BITSERY_LIKELY BITSERY_ATTRIBUTE(likely)
#else #else
#define BITSERY_LIKELY #define BITSERY_LIKELY
#endif #endif

View File

@@ -145,10 +145,7 @@ struct SwapImpl
return static_cast<uint16_t>((value & 0x00ff) << 8 | (value & 0xff00) >> 8); return static_cast<uint16_t>((value & 0x00ff) << 8 | (value & 0xff00) >> 8);
} }
static uint8_t exec(uint8_t value) static uint8_t exec(uint8_t value) { return value; }
{
return value;
}
}; };
template<typename TValue> template<typename TValue>

View File

@@ -62,8 +62,7 @@ struct NotDefinedType
template<typename T> template<typename T>
struct IsDefined struct IsDefined
: public std::integral_constant<bool, !std::is_same<NotDefinedType, T>::value> : public std::integral_constant<bool, !std::is_same<NotDefinedType, T>::value>
{ {};
};
} }
} }

View File

@@ -70,24 +70,19 @@ struct FtorExtValue : public Ext
template<typename Ext> template<typename Ext>
struct FtorExtValue1b : FtorExtValue<1, Ext> struct FtorExtValue1b : FtorExtValue<1, Ext>
{ {};
};
template<typename Ext> template<typename Ext>
struct FtorExtValue2b : FtorExtValue<2, Ext> struct FtorExtValue2b : FtorExtValue<2, Ext>
{ {};
};
template<typename Ext> template<typename Ext>
struct FtorExtValue4b : FtorExtValue<4, Ext> struct FtorExtValue4b : FtorExtValue<4, Ext>
{ {};
};
template<typename Ext> template<typename Ext>
struct FtorExtValue8b : FtorExtValue<8, Ext> struct FtorExtValue8b : FtorExtValue<8, Ext>
{ {};
};
template<typename Ext> template<typename Ext>
struct FtorExtValue16b : FtorExtValue<16, Ext> struct FtorExtValue16b : FtorExtValue<16, Ext>
{ {};
};
template<typename Ext> template<typename Ext>
struct FtorExtObject : public Ext struct FtorExtObject : public Ext
@@ -105,8 +100,7 @@ struct FtorExtObject : public Ext
// SelectSerializeFnc<MyDerivedClass>:UseMemberFnc {}; // SelectSerializeFnc<MyDerivedClass>:UseMemberFnc {};
template<typename T> template<typename T>
struct SelectSerializeFnc : std::integral_constant<int, 0> struct SelectSerializeFnc : std::integral_constant<int, 0>
{ {};
};
// types you need to inherit from when specializing SelectSerializeFnc class // types you need to inherit from when specializing SelectSerializeFnc class
struct UseNonMemberFnc : std::integral_constant<int, 1> struct UseNonMemberFnc : std::integral_constant<int, 1>
@@ -120,20 +114,17 @@ namespace details {
template<typename T> template<typename T>
struct IsContainerTraitsDefined struct IsContainerTraitsDefined
: public IsDefined<typename traits::ContainerTraits<T>::TValue> : public IsDefined<typename traits::ContainerTraits<T>::TValue>
{ {};
};
template<typename T> template<typename T>
struct IsTextTraitsDefined struct IsTextTraitsDefined
: public IsDefined<typename traits::TextTraits<T>::TValue> : public IsDefined<typename traits::TextTraits<T>::TValue>
{ {};
};
template<typename Ext, typename T> template<typename Ext, typename T>
struct IsExtensionTraitsDefined struct IsExtensionTraitsDefined
: public IsDefined<typename traits::ExtensionTraits<Ext, T>::TValue> : public IsDefined<typename traits::ExtensionTraits<Ext, T>::TValue>
{ {};
};
#ifdef _MSC_VER #ifdef _MSC_VER
// helper types for HasSerializeFunction // helper types for HasSerializeFunction
@@ -151,8 +142,7 @@ struct HasSerializeFunctionHelper
}; };
template<typename S, typename T> template<typename S, typename T>
struct HasSerializeFunction : HasSerializeFunctionHelper<S, T>::type struct HasSerializeFunction : HasSerializeFunctionHelper<S, T>::type
{ {};
};
// helper types for HasSerializeMethod // helper types for HasSerializeMethod
template<typename S, typename T> template<typename S, typename T>
@@ -169,8 +159,7 @@ struct HasSerializeMethodHelper
}; };
template<typename S, typename T> template<typename S, typename T>
struct HasSerializeMethod : HasSerializeMethodHelper<S, T>::type struct HasSerializeMethod : HasSerializeMethodHelper<S, T>::type
{ {};
};
// helper types for IsBriefSyntaxIncluded // helper types for IsBriefSyntaxIncluded
template<typename S, typename T> template<typename S, typename T>
@@ -188,8 +177,7 @@ struct IsBriefSyntaxIncludedHelper
template<typename S, typename T> template<typename S, typename T>
struct IsBriefSyntaxIncluded : IsBriefSyntaxIncludedHelper<S, T>::type struct IsBriefSyntaxIncluded : IsBriefSyntaxIncludedHelper<S, T>::type
{ {};
};
#else #else
// helper metafunction, that is added to c++17 // helper metafunction, that is added to c++17
template<typename... Ts> template<typename... Ts>
@@ -202,8 +190,7 @@ using void_t = typename make_void<Ts...>::type;
template<typename, typename, typename = void> template<typename, typename, typename = void>
struct HasSerializeFunction : std::false_type struct HasSerializeFunction : std::false_type
{ {};
};
template<typename S, typename T> template<typename S, typename T>
struct HasSerializeFunction< struct HasSerializeFunction<
@@ -211,13 +198,11 @@ struct HasSerializeFunction<
T, T,
void_t<decltype(serialize(std::declval<S&>(), std::declval<T&>()))>> void_t<decltype(serialize(std::declval<S&>(), std::declval<T&>()))>>
: std::true_type : std::true_type
{ {};
};
template<typename, typename, typename = void> template<typename, typename, typename = void>
struct HasSerializeMethod : std::false_type struct HasSerializeMethod : std::false_type
{ {};
};
template<typename S, typename T> template<typename S, typename T>
struct HasSerializeMethod< struct HasSerializeMethod<
@@ -225,14 +210,12 @@ struct HasSerializeMethod<
T, T,
void_t<decltype(Access::serialize(std::declval<S&>(), std::declval<T&>()))>> void_t<decltype(Access::serialize(std::declval<S&>(), std::declval<T&>()))>>
: std::true_type : std::true_type
{ {};
};
// this solution doesn't work with visual studio, but is more elegant // this solution doesn't work with visual studio, but is more elegant
template<typename, typename, typename = void> template<typename, typename, typename = void>
struct IsBriefSyntaxIncluded : std::false_type struct IsBriefSyntaxIncluded : std::false_type
{ {};
};
template<typename S, typename T> template<typename S, typename T>
struct IsBriefSyntaxIncluded< struct IsBriefSyntaxIncluded<
@@ -240,8 +223,7 @@ struct IsBriefSyntaxIncluded<
T, T,
void_t<decltype(processBriefSyntax(std::declval<S&>(), std::declval<T&&>()))>> void_t<decltype(processBriefSyntax(std::declval<S&>(), std::declval<T&&>()))>>
: std::true_type : std::true_type
{ {};
};
#endif #endif
// used for extensions when extension TValue = void // used for extensions when extension TValue = void
@@ -257,8 +239,7 @@ struct IsFundamentalType
std::is_enum<T>::value || std::is_enum<T>::value ||
std::is_floating_point<T>::value || std::is_floating_point<T>::value ||
std::is_integral<T>::value> std::is_integral<T>::value>
{ {};
};
template<typename T, typename Integral = void> template<typename T, typename Integral = void>
struct IntegralFromFundamental struct IntegralFromFundamental
@@ -372,16 +353,14 @@ struct BriefSyntaxFunction
template<int Index, typename... Conds> template<int Index, typename... Conds>
struct FindIndex : std::integral_constant<int, Index> struct FindIndex : std::integral_constant<int, Index>
{ {};
};
template<int Index, typename Cond, typename... Conds> template<int Index, typename Cond, typename... Conds>
struct FindIndex<Index, Cond, Conds...> struct FindIndex<Index, Cond, Conds...>
: std::conditional<Cond::value, : std::conditional<Cond::value,
std::integral_constant<int, Index>, std::integral_constant<int, Index>,
FindIndex<Index + 1, Conds...>>::type FindIndex<Index + 1, Conds...>>::type
{ {};
};
template<typename T, typename Tuple> template<typename T, typename Tuple>
struct GetConvertibleTypeIndexFromTuple; struct GetConvertibleTypeIndexFromTuple;
@@ -389,8 +368,7 @@ struct GetConvertibleTypeIndexFromTuple;
template<typename T, typename... Us> template<typename T, typename... Us>
struct GetConvertibleTypeIndexFromTuple<T, std::tuple<Us...>> struct GetConvertibleTypeIndexFromTuple<T, std::tuple<Us...>>
: FindIndex<0, std::is_convertible<Us&, T&>...> : FindIndex<0, std::is_convertible<Us&, T&>...>
{ {};
};
template<typename T, typename Tuple> template<typename T, typename Tuple>
struct IsExistsConvertibleTupleType; struct IsExistsConvertibleTupleType;
@@ -401,8 +379,7 @@ struct IsExistsConvertibleTupleType<T, std::tuple<Us...>>
bool, bool,
GetConvertibleTypeIndexFromTuple<T, std::tuple<Us...>>::value != GetConvertibleTypeIndexFromTuple<T, std::tuple<Us...>>::value !=
sizeof...(Us)> sizeof...(Us)>
{ {};
};
/* /*
* get context from internal or external, and check if it's convertible or not * get context from internal or external, and check if it's convertible or not
@@ -549,13 +526,11 @@ protected:
template<typename T, template<typename...> class Template> template<typename T, template<typename...> class Template>
struct IsSpecializationOf : std::false_type struct IsSpecializationOf : std::false_type
{ {};
};
template<template<typename...> class Template, typename... Args> template<template<typename...> class Template, typename... Args>
struct IsSpecializationOf<Template<Args...>, Template> : std::true_type struct IsSpecializationOf<Template<Args...>, Template> : std::true_type
{ {};
};
} }
} }

View File

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

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)
@@ -198,10 +202,24 @@ struct SmartPtrOwnerManager
state.obj = std::shared_ptr<TElement>(obj); 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) static void loadFromSharedState(TSharedState& state, T& obj)
{ {
// reinterpret_pointer_cast is only since c++17 auto v = state.obj.get();
auto p = reinterpret_cast<TElement*>(state.obj.get()); auto p = static_cast<TElement*>(v);
obj = std::shared_ptr<TElement>(state.obj, p);
}
static void loadFromSharedStatePolymorphic(TSharedState& state,
T& obj,
const PolymorphicHandlerBase&)
{
auto v = state.obj.get();
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;
}; };
@@ -117,16 +118,23 @@ struct PLCInfoSerializer : PLCInfo
size_t id; size_t id;
}; };
struct ObserverRef
{
std::reference_wrapper<void*> obj;
size_t baseTypeId;
};
struct PLCInfoDeserializer : PLCInfo struct PLCInfoDeserializer : PLCInfo
{ {
PLCInfoDeserializer(void* ptr, PLCInfoDeserializer(void* ptr,
size_t ownerTypeId_,
PointerOwnershipType ownershipType_, PointerOwnershipType ownershipType_,
MemResourceBase* memResource_) MemResourceBase* memResource_)
: PLCInfo(ownershipType_) : PLCInfo(ownershipType_)
, ownerPtr{ ptr } , ownerPtr{ ptr }
, ownerTypeId{ ownerTypeId_ }
, memResource{ memResource_ } , memResource{ memResource_ }
, observersList{ StdPolyAlloc<std::reference_wrapper<void*>>{ , observersList{ StdPolyAlloc<ObserverRef>{ memResource_ } } {};
memResource_ } } {};
// need to override these explicitly because we have pointer member // need to override these explicitly because we have pointer member
PLCInfoDeserializer(const PLCInfoDeserializer&) = delete; PLCInfoDeserializer(const PLCInfoDeserializer&) = delete;
@@ -137,30 +145,12 @@ struct PLCInfoDeserializer : PLCInfo
PLCInfoDeserializer& operator=(PLCInfoDeserializer&&) = default; PLCInfoDeserializer& operator=(PLCInfoDeserializer&&) = default;
void processOwner(void* ptr)
{
ownerPtr = ptr;
assert(ownershipType != PointerOwnershipType::Observer);
for (auto& o : observersList)
o.get() = ptr;
observersList.clear();
observersList.shrink_to_fit();
}
void processObserver(void*(&ptr))
{
if (ownerPtr) {
ptr = ownerPtr;
} else {
observersList.emplace_back(ptr);
}
}
void* ownerPtr; void* ownerPtr;
// used for polymorphic types in order to identify
// if shared objects can be assigned
size_t ownerTypeId;
MemResourceBase* memResource; MemResourceBase* memResource;
std::vector<std::reference_wrapper<void*>, std::vector<ObserverRef, StdPolyAlloc<ObserverRef>> observersList;
StdPolyAlloc<std::reference_wrapper<void*>>>
observersList;
std::unique_ptr<PointerSharedStateBase, PointerSharedStateDeleter> std::unique_ptr<PointerSharedStateBase, PointerSharedStateDeleter>
sharedState{}; sharedState{};
}; };
@@ -254,8 +244,8 @@ public:
PLCInfoDeserializer& getInfoById(size_t id, PointerOwnershipType ptrType) PLCInfoDeserializer& getInfoById(size_t id, PointerOwnershipType ptrType)
{ {
auto res = auto res = _idMap.emplace(
_idMap.emplace(id, PLCInfoDeserializer{ nullptr, ptrType, _memResource }); id, PLCInfoDeserializer{ nullptr, 0, ptrType, _memResource });
auto& ptrInfo = res.first->second; auto& ptrInfo = res.first->second;
if (!res.second) if (!res.second)
ptrInfo.update(ptrType); ptrInfo.update(ptrType);
@@ -329,8 +319,7 @@ public:
: std::integral_constant< : std::integral_constant<
bool, bool,
RTTI::template isPolymorphic<typename TPtrManager<T>::TElement>()> RTTI::template isPolymorphic<typename TPtrManager<T>::TElement>()>
{ {};
};
template<PointerOwnershipType Value> template<PointerOwnershipType Value>
using OwnershipType = std::integral_constant<PointerOwnershipType, Value>; using OwnershipType = std::integral_constant<PointerOwnershipType, Value>;
@@ -353,8 +342,8 @@ public:
if (ptr) { if (ptr) {
auto& ctx = ser.template context< auto& ctx = ser.template context<
pointer_utils::PointerLinkingContextSerialization>(); pointer_utils::PointerLinkingContextSerialization>();
auto& ptrInfo = auto& ptrInfo = ctx.getInfoByPtr(getRootPtr(ser, ptr, IsPolymorphic<T>{}),
ctx.getInfoByPtr(getBasePtr(ptr), TPtrManager<T>::getOwnership()); TPtrManager<T>::getOwnership());
details::writeSize(ser.adapter(), ptrInfo.id); details::writeSize(ser.adapter(), ptrInfo.id);
if (TPtrManager<T>::getOwnership() != PointerOwnershipType::Observer) { if (TPtrManager<T>::getOwnership() != PointerOwnershipType::Observer) {
if (!ptrInfo.isSharedProcessed) if (!ptrInfo.isSharedProcessed)
@@ -412,7 +401,7 @@ private:
const auto& ctx = des.template context<TPolymorphicContext<RTTI>>(); const auto& ctx = des.template context<TPolymorphicContext<RTTI>>();
auto ptr = TPtrManager<TObj>::getPtr(obj); auto ptr = TPtrManager<TObj>::getPtr(obj);
TPtrManager<TObj>::destroyPolymorphic( TPtrManager<TObj>::destroyPolymorphic(
obj, memResource, ctx.getPolymorphicHandler(*ptr)); obj, memResource, ctx.getPolymorphicHandler(ptr));
} }
template<typename Des, typename TObj> template<typename Des, typename TObj>
@@ -427,16 +416,23 @@ private:
RTTI::template get<typename TPtrManager<TObj>::TElement>()); RTTI::template get<typename TPtrManager<TObj>::TElement>());
} }
template<typename T> template<typename Ser, typename T>
const void* getBasePtr(const T* ptr) const 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; 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> template<typename Ser, typename TPtr, typename Fnc>
void serializeImpl(Ser& ser, TPtr& ptr, Fnc&&, std::true_type) const void serializeImpl(Ser& ser, TPtr& ptr, Fnc&&, std::true_type) const
{ {
@@ -472,7 +468,13 @@ private:
memResource](const std::shared_ptr<PolymorphicHandlerBase>& handler) { memResource](const std::shared_ptr<PolymorphicHandlerBase>& handler) {
TPtrManager<T>::destroyPolymorphic(obj, memResource, handler); TPtrManager<T>::destroyPolymorphic(obj, memResource, handler);
}); });
ptrInfo.processOwner(TPtrManager<T>::getPtr(obj)); auto ptr = TPtrManager<T>::getPtr(obj);
// might be null in case data pointer is not valid
if (ptr) {
ptrInfo.ownerPtr = ptr;
ptrInfo.ownerTypeId = ctx.getPolymorphicHandler(ptr)->getDerivedTypeId();
processObserverListPolymorphic(des, ptrInfo, ctx);
}
} }
template<typename Des, typename T, typename Fnc> template<typename Des, typename T, typename Fnc>
@@ -485,17 +487,18 @@ private:
OwnershipType<PointerOwnershipType::Owner>) const OwnershipType<PointerOwnershipType::Owner>) const
{ {
auto ptr = TPtrManager<T>::getPtr(obj); auto ptr = TPtrManager<T>::getPtr(obj);
if (ptr) { if (!ptr) {
fnc(des, *ptr);
} else {
TPtrManager<T>::create( TPtrManager<T>::create(
obj, obj,
memResource, memResource,
RTTI::template get<typename TPtrManager<T>::TElement>()); RTTI::template get<typename TPtrManager<T>::TElement>());
ptr = TPtrManager<T>::getPtr(obj); ptr = TPtrManager<T>::getPtr(obj);
fnc(des, *ptr);
} }
ptrInfo.processOwner(ptr); fnc(des, *ptr);
ptrInfo.ownerPtr = ptr;
ptrInfo.ownerTypeId =
RTTI::template get<typename TPtrManager<T>::TElement>();
processObserverList(des, ptrInfo);
} }
template<typename Des, typename T, typename Fnc> template<typename Des, typename T, typename Fnc>
@@ -507,8 +510,11 @@ private:
std::true_type, std::true_type,
OwnershipType<PointerOwnershipType::SharedOwner>) const OwnershipType<PointerOwnershipType::SharedOwner>) const
{ {
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) { if (!ptrInfo.sharedState) {
const auto& ctx = des.template context<TPolymorphicContext<RTTI>>();
ctx.deserialize( ctx.deserialize(
des, des,
TPtrManager<T>::getPtr(obj), TPtrManager<T>::getPtr(obj),
@@ -522,12 +528,28 @@ private:
memResource](const std::shared_ptr<PolymorphicHandlerBase>& handler) { memResource](const std::shared_ptr<PolymorphicHandlerBase>& handler) {
TPtrManager<T>::destroyPolymorphic(obj, memResource, handler); TPtrManager<T>::destroyPolymorphic(obj, memResource, handler);
}); });
if (!ptrInfo.sharedState) if (!ptrInfo.sharedState) {
TPtrManager<T>::saveToSharedState( TPtrManager<T>::saveToSharedStatePolymorphic(
createAndGetSharedStateObj<T>(ptrInfo), obj); createAndGetSharedStateObj<T>(ptrInfo), obj);
}
ptrInfo.ownerPtr = TPtrManager<T>::getPtr(obj);
ptrInfo.ownerTypeId =
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.ownerTypeId;
}
if (auto hndl =
ctx.getPolymorphicHandler(baseTypeId, ptrInfo.sharedState->typeId)) {
TPtrManager<T>::loadFromSharedStatePolymorphic(
getSharedStateObj<T>(ptrInfo), obj, **hndl);
processObserverListPolymorphic(des, ptrInfo, ctx);
} else {
des.adapter().error(ReaderError::InvalidPointer);
} }
TPtrManager<T>::loadFromSharedState(getSharedStateObj<T>(ptrInfo), obj);
ptrInfo.processOwner(TPtrManager<T>::getPtr(obj));
} }
template<typename Des, typename T, typename Fnc> template<typename Des, typename T, typename Fnc>
@@ -539,6 +561,8 @@ private:
std::false_type, std::false_type,
OwnershipType<PointerOwnershipType::SharedOwner>) const OwnershipType<PointerOwnershipType::SharedOwner>) const
{ {
const size_t baseTypeId =
RTTI::template get<typename TPtrManager<T>::TElement>();
if (!ptrInfo.sharedState) { if (!ptrInfo.sharedState) {
auto ptr = TPtrManager<T>::getPtr(obj); auto ptr = TPtrManager<T>::getPtr(obj);
if (ptr) { if (ptr) {
@@ -553,9 +577,16 @@ private:
ptr = TPtrManager<T>::getPtr(obj); ptr = TPtrManager<T>::getPtr(obj);
} }
fnc(des, *ptr); fnc(des, *ptr);
ptrInfo.ownerTypeId =
RTTI::template get<typename TPtrManager<T>::TElement>();
ptrInfo.ownerPtr = TPtrManager<T>::getPtr(obj);
}
if (baseTypeId == ptrInfo.ownerTypeId) {
TPtrManager<T>::loadFromSharedState(getSharedStateObj<T>(ptrInfo), obj);
processObserverList(des, ptrInfo);
} else {
des.adapter().error(ReaderError::InvalidPointer);
} }
TPtrManager<T>::loadFromSharedState(getSharedStateObj<T>(ptrInfo), obj);
ptrInfo.processOwner(TPtrManager<T>::getPtr(obj));
} }
template<typename Des, typename T, typename Fnc, typename isPolymorph> template<typename Des, typename T, typename Fnc, typename isPolymorph>
@@ -577,17 +608,85 @@ private:
OwnershipType<PointerOwnershipType::SharedOwner>{}); OwnershipType<PointerOwnershipType::SharedOwner>{});
} }
template<typename Des, typename T, typename Fnc, typename isPolymorphic> template<typename Des, typename T, typename Fnc>
void deserializeImpl(MemResourceBase*, void deserializeImpl(MemResourceBase*,
PLCInfoDeserializer& ptrInfo, PLCInfoDeserializer& ptrInfo,
Des&, Des& des,
T& obj, T& obj,
Fnc&&, Fnc&&,
isPolymorphic, std::false_type,
OwnershipType<PointerOwnershipType::Observer>) const OwnershipType<PointerOwnershipType::Observer>) const
{ {
ptrInfo.processObserver( auto baseTypeId = RTTI::template get<typename TPtrManager<T>::TElement>();
reinterpret_cast<void*&>(TPtrManager<T>::getPtrRef(obj))); void*(&ptr) = reinterpret_cast<void*&>(TPtrManager<T>::getPtrRef(obj));
if (ptrInfo.ownerPtr) {
if (ptrInfo.ownerTypeId == baseTypeId) {
ptr = ptrInfo.ownerPtr;
} else {
des.adapter().error(ReaderError::InvalidPointer);
}
} else {
ptrInfo.observersList.emplace_back(ObserverRef{ ptr, baseTypeId });
}
}
template<typename Des, typename T, typename Fnc>
void deserializeImpl(MemResourceBase*,
PLCInfoDeserializer& ptrInfo,
Des& des,
T& obj,
Fnc&&,
std::true_type,
OwnershipType<PointerOwnershipType::Observer>) const
{
const auto& ctx = des.template context<TPolymorphicContext<RTTI>>();
const size_t baseTypeId =
RTTI::template get<typename TPtrManager<T>::TElement>();
void*(&ptr) = reinterpret_cast<void*&>(TPtrManager<T>::getPtrRef(obj));
if (ptrInfo.ownerPtr) {
if (auto hndl =
ctx.getPolymorphicHandler(baseTypeId, ptrInfo.ownerTypeId)) {
ptr = hndl->get()->fromDerivedToBasePtr(ptrInfo.ownerPtr);
} else {
des.adapter().error(ReaderError::InvalidPointer);
}
} else {
ptrInfo.observersList.emplace_back(ObserverRef{ ptr, baseTypeId });
}
}
template<typename Des>
void processObserverList(Des& des, PLCInfoDeserializer& ptrInfo) const
{
assert(ptrInfo.ownershipType != PointerOwnershipType::Observer);
for (auto& o : ptrInfo.observersList) {
if (ptrInfo.ownerTypeId == o.baseTypeId) {
o.obj.get() = ptrInfo.ownerPtr;
} else {
des.adapter().error(ReaderError::InvalidPointer);
}
}
ptrInfo.observersList.clear();
ptrInfo.observersList.shrink_to_fit();
}
template<typename Des>
void processObserverListPolymorphic(
Des& des,
PLCInfoDeserializer& ptrInfo,
const TPolymorphicContext<RTTI>& ctx) const
{
assert(ptrInfo.ownershipType != PointerOwnershipType::Observer);
for (auto& o : ptrInfo.observersList) {
if (auto hndl =
ctx.getPolymorphicHandler(o.baseTypeId, ptrInfo.ownerTypeId)) {
o.obj.get() = hndl->get()->fromDerivedToBasePtr(ptrInfo.ownerPtr);
} else {
des.adapter().error(ReaderError::InvalidPointer);
}
}
ptrInfo.observersList.clear();
ptrInfo.observersList.shrink_to_fit();
} }
template<typename T> template<typename T>

View File

@@ -35,8 +35,7 @@ namespace ext {
// helper type, that contains list of types // helper type, that contains list of types
template<typename...> template<typename...>
struct PolymorphicClassesList struct PolymorphicClassesList
{ {};
};
// specialize for your base class by deriving from PolymorphicDerivedClasses // specialize for your base class by deriving from PolymorphicDerivedClasses
// with list of derivatives that DIRECTLY inherits from your base class. // with list of derivatives that DIRECTLY inherits from your base class.
@@ -75,10 +74,20 @@ public:
virtual void process(void* ser, void* obj) const = 0; virtual void process(void* ser, 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 ~PolymorphicHandlerBase() = default; 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 class PolymorphicHandler : public PolymorphicHandlerBase
{ {
public: public:
@@ -98,6 +107,19 @@ public:
static_cast<TSerializer*>(ser)->object(*fromBase(obj)); 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)));
}
void* fromDerivedToBasePtr(void* obj) const final { return toBase(obj); }
size_t getDerivedTypeId() const final
{
return RTTI::template get<TDerived>();
}
private: private:
TDerived* fromBase(void* obj) const TDerived* fromBase(void* obj) const
{ {
@@ -110,6 +132,41 @@ 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&) const
{
assert(false);
return nullptr;
}
void destroy(const pointer_utils::PolyAllocWithTypeId&, void*) const
{
assert(false);
};
void process(void*, void*) const { assert(false); }
void* getRootPtr(const void*) const
{
assert(false);
return nullptr;
}
void* fromDerivedToBasePtr(void*) const final
{
assert(false);
return nullptr;
}
size_t getDerivedTypeId() const { return RTTI::template get<TDerived>(); };
};
template<typename RTTI> template<typename RTTI>
class PolymorphicContext class PolymorphicContext
{ {
@@ -138,18 +195,20 @@ private:
template<typename TSerializer, template<typename TSerializer,
template<typename> template<typename>
class THierarchy, class THierarchy,
typename TRoot,
typename TBase, typename TBase,
typename TDerived> typename TDerived>
void add() void add()
{ {
addToMap<TSerializer, TBase, TDerived>(std::is_abstract<TDerived>{}); addToMap<TSerializer, TRoot, TBase, TDerived>(std::is_abstract<TDerived>{});
addChilds<TSerializer, THierarchy, TBase, TDerived>( addChilds<TSerializer, THierarchy, TRoot, TBase, TDerived>(
typename THierarchy<TDerived>::Childs{}); typename THierarchy<TDerived>::Childs{});
} }
template<typename TSerializer, template<typename TSerializer,
template<typename> template<typename>
class THierarchy, class THierarchy,
typename TRoot,
typename TBase, typename TBase,
typename TDerived, typename TDerived,
typename T1, typename T1,
@@ -159,26 +218,30 @@ private:
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, TBase, T1>(); add<TSerializer, THierarchy, TRoot, TBase, T1>();
addChilds<TSerializer, THierarchy, TBase, TDerived>( addChilds<TSerializer, THierarchy, TRoot, TBase, TDerived>(
PolymorphicClassesList<Tn...>{}); PolymorphicClassesList<Tn...>{});
// iterate through derived class hierarchy as well add<TSerializer, THierarchy, TRoot, T1, T1>();
add<TSerializer, THierarchy, T1, T1>();
} }
template<typename TSerializer, template<typename TSerializer,
template<typename> template<typename>
class THierarchy, class THierarchy,
typename TRoot,
typename TBase, typename TBase,
typename TDerived> typename TDerived>
void addChilds(PolymorphicClassesList<>) void addChilds(PolymorphicClassesList<>)
{ {
} }
template<typename TSerializer, typename TBase, typename TDerived> template<typename TSerializer,
typename TRoot,
typename TBase,
typename TDerived>
void addToMap(std::false_type) void addToMap(std::false_type)
{ {
using THandler = PolymorphicHandler<RTTI, TSerializer, TBase, TDerived>; using THandler =
PolymorphicHandler<RTTI, TSerializer, TRoot, TBase, TDerived>;
BaseToDerivedKey key{ RTTI::template get<TBase>(), BaseToDerivedKey key{ RTTI::template get<TBase>(),
RTTI::template get<TDerived>() }; RTTI::template get<TDerived>() };
pointer_utils::StdPolyAlloc<THandler> alloc{ _memResource }; pointer_utils::StdPolyAlloc<THandler> alloc{ _memResource };
@@ -204,10 +267,25 @@ private:
} }
} }
template<typename TSerializer, typename TBase, typename TDerived> template<typename TSerializer,
typename TRoot,
typename TBase,
typename TDerived>
void addToMap(std::true_type) void addToMap(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));
} }
MemResourceBase* _memResource; MemResourceBase* _memResource;
@@ -270,7 +348,7 @@ public:
typename... Tn> typename... Tn>
void registerBasesList(PolymorphicClassesList<T1, Tn...>) void registerBasesList(PolymorphicClassesList<T1, Tn...>)
{ {
add<TSerializer, THierarchy, T1, T1>(); add<TSerializer, THierarchy, T1, T1, T1>();
registerBasesList<TSerializer, THierarchy>(PolymorphicClassesList<Tn...>{}); registerBasesList<TSerializer, THierarchy>(PolymorphicClassesList<Tn...>{});
} }
@@ -279,18 +357,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> template<typename Serializer, typename TBase>
void serialize(Serializer& ser, TBase& obj) const void serialize(Serializer& ser, TBase& obj) const
{ {
@@ -339,7 +405,7 @@ public:
// if object is null or different type, create new and assign it // if object is null or different type, create new and assign it
if (obj == nullptr || RTTI::template get<TBase>(*obj) != derivedHash) { if (obj == nullptr || RTTI::template get<TBase>(*obj) != derivedHash) {
if (obj) { if (obj) {
destroyFnc(getPolymorphicHandler(*obj)); destroyFnc(getPolymorphicHandler(obj));
} }
obj = createFnc(handler); obj = createFnc(handler);
} }
@@ -350,12 +416,33 @@ public:
template<typename TBase> template<typename TBase>
const std::shared_ptr<PolymorphicHandlerBase>& getPolymorphicHandler( const std::shared_ptr<PolymorphicHandlerBase>& getPolymorphicHandler(
TBase& obj) const TBase* obj) const
{ {
auto deleteHandlerIt = _baseToDerivedMap.find(BaseToDerivedKey{ auto it = _baseToDerivedMap.find(BaseToDerivedKey{
RTTI::template get<TBase>(), RTTI::template get<TBase>(obj) }); RTTI::template get<TBase>(), RTTI::template get<TBase>(*obj) });
assert(deleteHandlerIt != _baseToDerivedMap.end()); assert(it != _baseToDerivedMap.end());
return deleteHandlerIt->second; 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::shared_ptr<PolymorphicHandlerBase>* getPolymorphicHandler(
size_t baseTypeId,
size_t derivedTypeId) const
{
auto it =
_baseToDerivedMap.find(BaseToDerivedKey{ baseTypeId, derivedTypeId });
if (it == _baseToDerivedMap.end()) {
return nullptr;
}
return &it->second;
} }
}; };

View File

@@ -32,14 +32,12 @@ namespace traits {
template<typename T, size_t N> template<typename T, size_t N>
struct ContainerTraits<std::array<T, N>> struct ContainerTraits<std::array<T, N>>
: public StdContainer<std::array<T, N>, false, true> : public StdContainer<std::array<T, N>, false, true>
{ {};
};
template<typename T, size_t N> template<typename T, size_t N>
struct BufferAdapterTraits<std::array<T, N>> struct BufferAdapterTraits<std::array<T, N>>
: public StdContainerForBufferAdapter<std::array<T, N>> : public StdContainerForBufferAdapter<std::array<T, N>>
{ {};
};
} }
} }

View File

@@ -33,8 +33,7 @@ namespace traits {
template<typename T, typename Allocator> template<typename T, typename Allocator>
struct ContainerTraits<std::deque<T, Allocator>> struct ContainerTraits<std::deque<T, Allocator>>
: public StdContainer<std::deque<T, Allocator>, true, false> : public StdContainer<std::deque<T, Allocator>, true, false>
{ {};
};
} }

View File

@@ -33,8 +33,7 @@ namespace traits {
template<typename T, typename Allocator> template<typename T, typename Allocator>
struct ContainerTraits<std::list<T, Allocator>> struct ContainerTraits<std::list<T, Allocator>>
: public StdContainer<std::list<T, Allocator>, true, false> : public StdContainer<std::list<T, Allocator>, true, false>
{ {};
};
} }

View File

@@ -36,8 +36,7 @@ namespace traits {
template<typename CharT, typename Traits, typename Allocator> template<typename CharT, typename Traits, typename Allocator>
struct ContainerTraits<std::basic_string<CharT, Traits, Allocator>> struct ContainerTraits<std::basic_string<CharT, Traits, Allocator>>
: public StdContainer<std::basic_string<CharT, Traits, Allocator>, true, true> : public StdContainer<std::basic_string<CharT, Traits, Allocator>, true, true>
{ {};
};
template<typename CharT, typename Traits, typename Allocator> template<typename CharT, typename Traits, typename Allocator>
struct TextTraits<std::basic_string<CharT, Traits, Allocator>> struct TextTraits<std::basic_string<CharT, Traits, Allocator>>
@@ -72,8 +71,7 @@ template<typename CharT, typename Traits, typename Allocator>
struct BufferAdapterTraits<std::basic_string<CharT, Traits, Allocator>> struct BufferAdapterTraits<std::basic_string<CharT, Traits, Allocator>>
: public StdContainerForBufferAdapter< : public StdContainerForBufferAdapter<
std::basic_string<CharT, Traits, Allocator>> std::basic_string<CharT, Traits, Allocator>>
{ {};
};
} }

View File

@@ -32,21 +32,18 @@ namespace traits {
template<typename T, typename Allocator> template<typename T, typename Allocator>
struct ContainerTraits<std::vector<T, Allocator>> struct ContainerTraits<std::vector<T, Allocator>>
: public StdContainer<std::vector<T, Allocator>, true, true> : public StdContainer<std::vector<T, Allocator>, true, true>
{ {};
};
// bool vector is not contiguous, do not copy it directly to buffer // bool vector is not contiguous, do not copy it directly to buffer
template<typename Allocator> template<typename Allocator>
struct ContainerTraits<std::vector<bool, Allocator>> struct ContainerTraits<std::vector<bool, Allocator>>
: public StdContainer<std::vector<bool, Allocator>, true, false> : public StdContainer<std::vector<bool, Allocator>, true, false>
{ {};
};
template<typename T, typename Allocator> template<typename T, typename Allocator>
struct BufferAdapterTraits<std::vector<T, Allocator>> struct BufferAdapterTraits<std::vector<T, Allocator>>
: public StdContainerForBufferAdapter<std::vector<T, Allocator>> : public StdContainerForBufferAdapter<std::vector<T, Allocator>>
{ {};
};
} }

View File

@@ -1,11 +0,0 @@
# Compiler specific patches
This folder will provide patches for various C++ compilers that are not C++11 compatible yet. This allows providing any fix for any compiler, without polluting core library with compiler-specific fixes.
A patch can be applied either with `git apply` or `patch` command, like this:
```bash
git apply patches/<patch_name>
patch -p1 < patches/<patch_name>
```
* [centos7_gcc4.8.2.diff](centos7_gcc4.8.2.diff) in this version, unordered_map is not fully C++11 compatible yet. It is lacking some constructors that accept allocator, and isn't using `std::allocator_traits`.

View File

@@ -1,119 +0,0 @@
diff --git a/include/bitsery/details/serialization_common.h b/include/bitsery/details/serialization_common.h
index 6d5a441..462cee2 100644
--- a/include/bitsery/details/serialization_common.h
+++ b/include/bitsery/details/serialization_common.h
@@ -380,7 +380,7 @@ namespace bitsery {
template <typename ... TArgs>
explicit AdapterAndContextRef(Context& ctx, TArgs&& ... args)
: _adapter{std::forward<TArgs>(args)...},
- _context{ctx}
+ _context(ctx)
{
}
diff --git a/include/bitsery/ext/inheritance.h b/include/bitsery/ext/inheritance.h
index f4c6655..5cd44ab 100644
--- a/include/bitsery/ext/inheritance.h
+++ b/include/bitsery/ext/inheritance.h
@@ -36,7 +36,7 @@ namespace bitsery {
class InheritanceContext {
public:
explicit InheritanceContext(MemResourceBase* memResource = nullptr)
- :_virtualBases{pointer_utils::StdPolyAlloc<const void*>{memResource}}
+ :_virtualBases{0, std::hash<const void*>{}, std::equal_to<const void*>{}, pointer_utils::StdPolyAlloc<const void*>{memResource}}
{}
InheritanceContext(const InheritanceContext&) = delete;
InheritanceContext&operator = (const InheritanceContext&) = delete;
diff --git a/include/bitsery/ext/utils/memory_resource.h b/include/bitsery/ext/utils/memory_resource.h
index 472965a..18b3f31 100644
--- a/include/bitsery/ext/utils/memory_resource.h
+++ b/include/bitsery/ext/utils/memory_resource.h
@@ -24,6 +24,7 @@
#define BITSERY_EXT_MEMORY_RESOURCE_H
#include "../../details/serialization_common.h"
+#include <cstddef>
#include <new>
namespace bitsery {
@@ -128,6 +129,40 @@ namespace bitsery {
public:
using value_type = T;
+ using pointer = T*;
+ using const_pointer = const T*;
+ using reference = T&;
+ using const_reference = const T&;
+ using size_type = size_t;
+ using difference_type = ptrdiff_t;
+
+ size_t max_size() const noexcept {
+ return std::numeric_limits<size_t>::max() / sizeof(value_type);
+ }
+
+ void construct(T *p, const T &val) {
+ new((void *) p) T(val);
+ }
+
+ template<class U, class... Args>
+ void construct(U *p, Args &&... args) {
+ new((void *) p) U(std::forward<Args>(args)...);
+ }
+
+ void destroy(T *p) {
+ p->~T();
+ }
+
+ template<class U>
+ void destroy(U *p) {
+ p->~U();
+ }
+
+ template<typename U>
+ struct rebind {
+ using other = StdPolyAlloc<U>;
+ };
+
explicit constexpr StdPolyAlloc(MemResourceBase* memResource)
:_alloc{memResource} {}
explicit constexpr StdPolyAlloc(PolyAllocWithTypeId alloc) : _alloc{alloc} {}
diff --git a/include/bitsery/ext/utils/pointer_utils.h b/include/bitsery/ext/utils/pointer_utils.h
index f6f90da..6b65600 100644
--- a/include/bitsery/ext/utils/pointer_utils.h
+++ b/include/bitsery/ext/utils/pointer_utils.h
@@ -153,7 +153,7 @@ namespace bitsery {
public:
explicit PointerLinkingContextSerialization(MemResourceBase* memResource = nullptr)
: _currId{0},
- _ptrMap{StdPolyAlloc<std::pair<const void* const, PLCInfoSerializer>>{memResource}} {}
+ _ptrMap{0, std::hash<const void*>{}, std::equal_to<const void*>{}, StdPolyAlloc<std::pair<const void* const, PLCInfoSerializer>>{memResource}} {}
PointerLinkingContextSerialization(const PointerLinkingContextSerialization&) = delete;
@@ -198,7 +198,7 @@ namespace bitsery {
public:
explicit PointerLinkingContextDeserialization(MemResourceBase* memResource = nullptr)
: _memResource{memResource},
- _idMap{StdPolyAlloc<std::pair<const size_t, PLCInfoDeserializer>>{memResource}} {}
+ _idMap{0, std::hash<size_t>{}, std::equal_to<size_t>{}, StdPolyAlloc<std::pair<const size_t, PLCInfoDeserializer>>{memResource}} {}
PointerLinkingContextDeserialization(const PointerLinkingContextDeserialization&) = delete;
diff --git a/include/bitsery/ext/utils/polymorphism_utils.h b/include/bitsery/ext/utils/polymorphism_utils.h
index 6678230..a2cef4d 100644
--- a/include/bitsery/ext/utils/polymorphism_utils.h
+++ b/include/bitsery/ext/utils/polymorphism_utils.h
@@ -185,11 +185,8 @@ namespace bitsery {
explicit PolymorphicContext(MemResourceBase* memResource = nullptr)
:_memResource{memResource},
- _baseToDerivedMap{pointer_utils::StdPolyAlloc<std::pair<const BaseToDerivedKey,
- std::shared_ptr<PolymorphicHandlerBase>>>{memResource}},
- _baseToDerivedArray{pointer_utils::StdPolyAlloc<std::pair<const size_t,
- std::vector<size_t, pointer_utils::StdPolyAlloc<size_t>>>>{memResource}}
- {}
+ _baseToDerivedMap{0, BaseToDerivedKeyHashier{}, std::equal_to<BaseToDerivedKey>{}, pointer_utils::StdPolyAlloc<std::pair<const BaseToDerivedKey, std::shared_ptr<PolymorphicHandlerBase>>>{memResource}},
+ _baseToDerivedArray{0, 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>>>>{memResource}} {}
PolymorphicContext(const PolymorphicContext& ) = delete;
PolymorphicContext& operator = (const PolymorphicContext&) = delete;

View File

@@ -306,8 +306,7 @@ using AdapterInputTypes =
template<typename TConfig> template<typename TConfig>
class InputAll : public AdapterConfig<TConfig> class InputAll : public AdapterConfig<TConfig>
{ {};
};
TYPED_TEST_SUITE(InputAll, AdapterInputTypes, ); TYPED_TEST_SUITE(InputAll, AdapterInputTypes, );
@@ -476,8 +475,7 @@ using AdapterOutputTypes =
template<typename TConfig> template<typename TConfig>
class OutputAll : public AdapterConfig<TConfig> class OutputAll : public AdapterConfig<TConfig>
{ {};
};
TYPED_TEST_SUITE(OutputAll, AdapterOutputTypes, ); TYPED_TEST_SUITE(OutputAll, AdapterOutputTypes, );

View File

@@ -38,9 +38,9 @@
#include <bitsery/brief_syntax/unordered_set.h> #include <bitsery/brief_syntax/unordered_set.h>
#include <bitsery/brief_syntax/vector.h> #include <bitsery/brief_syntax/vector.h>
#if __cplusplus > 201402L #if __cplusplus > 201402L
#include <bitsery/brief_syntax/optional.h>
#include <bitsery/brief_syntax/tuple.h> #include <bitsery/brief_syntax/tuple.h>
#include <bitsery/brief_syntax/variant.h> #include <bitsery/brief_syntax/variant.h>
#include <bitsery/brief_syntax/optional.h>
#if __cplusplus > 202002L #if __cplusplus > 202002L
#include <bitsery/brief_syntax/bitset.h> #include <bitsery/brief_syntax/bitset.h>
#endif #endif

View File

@@ -320,8 +320,7 @@ namespace ext {
template<> template<>
struct PolymorphicBaseClass<PolymorphicNDCBase> struct PolymorphicBaseClass<PolymorphicNDCBase>
: PolymorphicDerivedClasses<PolymorphicNDC1, PolymorphicNDC2> : PolymorphicDerivedClasses<PolymorphicNDC1, PolymorphicNDC2>
{ {};
};
} }
} }

View File

@@ -214,8 +214,7 @@ TYPED_TEST(SerializeContainerFixedSizeArithmeticTypes, ArithmeticValues)
template<typename T> template<typename T>
class SerializeContainerFixedSizeCompositeTypes class SerializeContainerFixedSizeCompositeTypes
: public SerializeContainerFixedSizeArithmeticTypes<T> : public SerializeContainerFixedSizeArithmeticTypes<T>
{ {};
};
using StaticContainersWithCompositeTypes = using StaticContainersWithCompositeTypes =
::testing::Types<std::array<MyStruct1, 4>, MyStruct1[4]>; ::testing::Types<std::array<MyStruct1, 4>, MyStruct1[4]>;

View File

@@ -356,13 +356,11 @@ struct DerivedMemberSerialize : public BaseNonMemberSerialize
namespace bitsery { namespace bitsery {
template<> template<>
struct SelectSerializeFnc<DerivedPrivateBase> : UseNonMemberFnc struct SelectSerializeFnc<DerivedPrivateBase> : UseNonMemberFnc
{ {};
};
template<> template<>
struct SelectSerializeFnc<DerivedMemberSerialize> : UseMemberFnc struct SelectSerializeFnc<DerivedMemberSerialize> : UseMemberFnc
{ {};
};
} }
TEST( TEST(

View File

@@ -458,6 +458,23 @@ TEST_F(SerializeExtensionPointerDeserialization, PointerObserver)
EXPECT_THAT(pr3, Eq(&r3)); EXPECT_THAT(pr3, Eq(&r3));
} }
TEST_F(SerializeExtensionPointerDeserialization,
PointerObserverAndOwnerTypeMustBeTheSame)
{
// serialize as if we have two same objects
auto& ser = createSerializer();
ser.ext2b(d1, ReferencedByPointer{});
ser.ext2b(pd1, PointerObserver{});
auto& des = createDeserializer();
// but actual implementation expects distinct objects
des.ext2b(r1, ReferencedByPointer{});
des.ext4b(pr2, PointerObserver{});
EXPECT_THAT(isPointerContextValid(), Eq(true));
EXPECT_THAT(sctx1.des->adapter().error(),
Eq(bitsery::ReaderError::InvalidPointer));
}
struct Test1Data struct Test1Data
{ {
std::vector<MyStruct1> vdata; std::vector<MyStruct1> vdata;

View File

@@ -147,21 +147,17 @@ namespace ext {
template<> template<>
struct PolymorphicBaseClass<Base> 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>
: PolymorphicDerivedClasses<MultipleVirtualInheritance> : PolymorphicDerivedClasses<MultipleVirtualInheritance>
{ {};
};
} }
} }
@@ -373,3 +369,62 @@ TEST_F(SerializeExtensionPointerPolymorphicTypes,
EXPECT_THAT(sctx.des->adapter().error(), EXPECT_THAT(sctx.des->adapter().error(),
Eq(bitsery::ReaderError::InvalidPointer)); Eq(bitsery::ReaderError::InvalidPointer));
} }
TEST_F(SerializeExtensionPointerPolymorphicTypes,
SameObjectIsCorrectlyIdentifiedEvenIfObserverHasDifferentBase)
{
MultipleVirtualInheritance md;
Derived2* derivedData = &md;
EXPECT_THAT(static_cast<void*>(&md),
::testing::Ne(static_cast<void*>(derivedData)));
auto& ser = createSerializer();
ser.ext(md, ReferencedByPointer{});
ser.ext(derivedData, PointerObserver{});
EXPECT_THAT(isPointerContextValid(), Eq(true));
}
TEST_F(SerializeExtensionPointerPolymorphicTypes,
CheckIfOwnerTypeIsAssignableToObserverType)
{
MultipleVirtualInheritance md;
Derived2* derivedData = &md;
auto& ser = createSerializer();
ser.ext(&md, PointerOwner{});
ser.ext(derivedData, PointerObserver{});
MultipleVirtualInheritance* res1 = nullptr;
NoRelationshipSpecifiedDerived* res2 = nullptr;
auto& des = createDeserializer();
des.ext(res1, PointerOwner{});
des.ext(res2, PointerObserver{});
EXPECT_THAT(res1, ::testing::NotNull());
EXPECT_THAT(res2, ::testing::IsNull());
EXPECT_THAT(sctx.des->adapter().error(),
Eq(bitsery::ReaderError::InvalidPointer));
}
TEST_F(SerializeExtensionPointerPolymorphicTypes, OwnerIsCastObserverType)
{
MultipleVirtualInheritance md{ 1, 2, 3, 4 };
Derived2* derivedData = &md;
auto& ser = createSerializer();
ser.ext(&md, PointerOwner{});
ser.ext(derivedData, PointerObserver{});
MultipleVirtualInheritance* res1 = nullptr;
Base* res2 = nullptr;
auto& des = createDeserializer();
des.ext(res1, PointerOwner{});
des.ext(res2, PointerObserver{});
EXPECT_THAT(res1, ::testing::NotNull());
EXPECT_THAT(res2, ::testing::NotNull());
EXPECT_THAT(res2->x, Eq(1));
}

View File

@@ -152,14 +152,12 @@ namespace ext {
template<> template<>
struct PolymorphicBaseClass<Base> struct PolymorphicBaseClass<Base>
: PolymorphicDerivedClasses<Derived1, Derived2> : PolymorphicDerivedClasses<Derived1, Derived2>
{ {};
};
template<> template<>
struct PolymorphicBaseClass<PolyPtrWithPolyPtrBase> struct PolymorphicBaseClass<PolyPtrWithPolyPtrBase>
: PolymorphicDerivedClasses<DerivedPolyPtrWithPolyPtr> : PolymorphicDerivedClasses<DerivedPolyPtrWithPolyPtr>
{ {};
};
} }
} }

View File

@@ -23,6 +23,8 @@
#include <bitsery/ext/inheritance.h> #include <bitsery/ext/inheritance.h>
#include <bitsery/ext/pointer.h> #include <bitsery/ext/pointer.h>
#include <bitsery/ext/std_smart_ptr.h> #include <bitsery/ext/std_smart_ptr.h>
#include <bitsery/traits/string.h>
#include <bitsery/traits/vector.h>
#include "serialization_test_utils.h" #include "serialization_test_utils.h"
#include <gmock/gmock.h> #include <gmock/gmock.h>
@@ -77,6 +79,27 @@ serialize(S& s, Derived& o)
s.value1b(o.y); 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 struct MoreDerived : Derived
{ {
uint8_t z{}; uint8_t z{};
@@ -106,13 +129,11 @@ namespace ext {
template<> template<>
struct PolymorphicBaseClass<Base> : PolymorphicDerivedClasses<Derived> struct PolymorphicBaseClass<Base> : PolymorphicDerivedClasses<Derived>
{ {};
};
template<> template<>
struct PolymorphicBaseClass<Derived> : PolymorphicDerivedClasses<MoreDerived> struct PolymorphicBaseClass<Derived> : PolymorphicDerivedClasses<MoreDerived>
{ {};
};
} }
} }
@@ -743,6 +764,103 @@ TEST_F(SerializeExtensionStdSmartSharedPtr,
EXPECT_THAT(baseRes1.use_count(), Eq(0)); 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 struct TestSharedFromThis
: public std::enable_shared_from_this<TestSharedFromThis> : public std::enable_shared_from_this<TestSharedFromThis>
{ {