No more messing up enable_shared_from_this refcounts
Modified the polymorphic saving of pointers to use a second shared_ptr to manage the refcount of the wrapper proper polymorphic shared_ptr (previously we were using an empty deleter). The result of this is that the wrapper doesn't actually manage the pointer it holds, it just allows access to it so long as there is still a refcount in the second refcount pointer. Modified the polymorphic wrapper and enable shared from this state saver to be RAII style.
This commit is contained in:
@@ -202,20 +202,44 @@ namespace cereal
|
||||
}
|
||||
}
|
||||
|
||||
//! Constructs the appropriate shared_ptr for the polymorphic type
|
||||
/*! @param dptr A void pointer to the contents of the shared_ptr to serialize
|
||||
@return A shared_ptr pointing to dptr with an empty deleter */
|
||||
static inline std::shared_ptr<T const> createSharedPtr( void const * dptr )
|
||||
//! Holds a properly typed shared_ptr to the polymorphic type
|
||||
class PolymorphicSharedPointerWrapper
|
||||
{
|
||||
#ifdef _LIBCPP_VERSION
|
||||
// libc++ needs this hacky workaround, see http://llvm.org/bugs/show_bug.cgi?id=18843
|
||||
return std::shared_ptr<T const>(
|
||||
std::const_pointer_cast<T const>(
|
||||
std::shared_ptr<T>(static_cast<T *>(const_cast<void *>(dptr)), EmptyDeleter<T>())));
|
||||
#else // NOT _LIBCPP_VERSION
|
||||
return std::shared_ptr<T const>(static_cast<T const *>(dptr), EmptyDeleter<T const>());
|
||||
#endif // _LIBCPP_VERSION
|
||||
}
|
||||
public:
|
||||
/*! Wrap a raw polymorphic pointer in a shared_ptr to its true type
|
||||
|
||||
The wrapped pointer will not be responsible for ownership of the held pointer
|
||||
so it will not attempt to destroy it; instead the refcount of the wrapped
|
||||
pointer will be tied to a fake 'ownership pointer' that will do nothing
|
||||
when it ultimately goes out of scope.
|
||||
|
||||
The main reason for doing this, other than not to destroy the true object
|
||||
with our wrapper pointer, is to avoid meddling with the internal reference
|
||||
count in a polymorphic type that inherits from std::enable_shared_from_this.
|
||||
|
||||
@param dptr A void pointer to the contents of the shared_ptr to serialize */
|
||||
PolymorphicSharedPointerWrapper( void const * dptr ) : refCount()
|
||||
{
|
||||
#ifdef _LIBCPP_VERSION
|
||||
// libc++ needs this hacky workaround, see http://llvm.org/bugs/show_bug.cgi?id=18843
|
||||
wrappedPtr = std::shared_ptr<T const>(
|
||||
std::const_pointer_cast<T const>(
|
||||
std::shared_ptr<T>( refCount, static_cast<T *>(const_cast<void *>(dptr) ))));
|
||||
#else // NOT _LIBCPP_VERSION
|
||||
wrappedPtr = std::shared_ptr<T const>( refCount, static_cast<T const *>(dptr) );
|
||||
#endif // _LIBCPP_VERSION
|
||||
}
|
||||
|
||||
//! Get the wrapped shared_ptr */
|
||||
inline std::shared_ptr<T const> const & operator()() const
|
||||
{
|
||||
return wrappedPtr;
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<void> refCount; //!< The ownership pointer
|
||||
std::shared_ptr<T const> wrappedPtr; //!< The wrapped pointer
|
||||
};
|
||||
|
||||
//! Does the actual work of saving a polymorphic shared_ptr
|
||||
/*! This function will properly create a shared_ptr from the void * that is passed in
|
||||
@@ -227,12 +251,9 @@ namespace cereal
|
||||
@param dptr Pointer to the actual data held by the shared_ptr */
|
||||
static inline void savePolymorphicSharedPtr( Archive & ar, void const * dptr, std::true_type /* has_shared_from_this */ )
|
||||
{
|
||||
::cereal::memory_detail::EnableSharedHelper<T> helper( static_cast<T *>(const_cast<void *>(dptr)) );
|
||||
|
||||
auto const ptr = createSharedPtr( dptr );
|
||||
ar( _CEREAL_NVP("ptr_wrapper", memory_detail::make_ptr_wrapper(ptr)) );
|
||||
|
||||
helper.restore();
|
||||
::cereal::memory_detail::EnableSharedStateHelper<T> state( static_cast<T *>(const_cast<void *>(dptr)) );
|
||||
PolymorphicSharedPointerWrapper psptr( dptr );
|
||||
ar( _CEREAL_NVP("ptr_wrapper", memory_detail::make_ptr_wrapper( psptr() ) ) );
|
||||
}
|
||||
|
||||
//! Does the actual work of saving a polymorphic shared_ptr
|
||||
@@ -245,8 +266,8 @@ namespace cereal
|
||||
@param dptr Pointer to the actual data held by the shared_ptr */
|
||||
static inline void savePolymorphicSharedPtr( Archive & ar, void const * dptr, std::false_type /* has_shared_from_this */ )
|
||||
{
|
||||
auto const ptr = createSharedPtr( dptr );
|
||||
ar( _CEREAL_NVP("ptr_wrapper", memory_detail::make_ptr_wrapper(ptr)) );
|
||||
PolymorphicSharedPointerWrapper psptr( dptr );
|
||||
ar( _CEREAL_NVP("ptr_wrapper", memory_detail::make_ptr_wrapper( psptr() ) ) );
|
||||
}
|
||||
|
||||
//! Initialize the binding
|
||||
|
||||
@@ -99,15 +99,20 @@ namespace cereal
|
||||
|
||||
@code{.cpp}
|
||||
T * myActualPointer;
|
||||
EnableSharedHelper<T> helper( myActualPointer ); // save the state
|
||||
std::shared_ptr<T> myPtr( myActualPointer ); // modifies the internal weak_ptr
|
||||
helper.restore(); // good as new!
|
||||
{
|
||||
EnableSharedStateHelper<T> helper( myActualPointer ); // save the state
|
||||
std::shared_ptr<T> myPtr( myActualPointer ); // modifies the internal weak_ptr
|
||||
// helper restores state when it goes out of scope
|
||||
}
|
||||
@endcode
|
||||
|
||||
This is designed to be used in an RAII fashion - it will save state on construction
|
||||
and restore it on destruction.
|
||||
|
||||
@tparam T Type pointed to by shared_ptr
|
||||
@internal */
|
||||
template <class T>
|
||||
class EnableSharedHelper
|
||||
class EnableSharedStateHelper
|
||||
{
|
||||
// typedefs for parent type and storage type
|
||||
using BaseType = typename ::cereal::traits::get_shared_from_this_base<T>::type;
|
||||
@@ -117,7 +122,7 @@ namespace cereal
|
||||
public:
|
||||
//! Saves the state of some type inheriting from enable_shared_from_this
|
||||
/*! @param ptr The raw pointer held by the shared_ptr */
|
||||
inline EnableSharedHelper( T * ptr ) :
|
||||
inline EnableSharedStateHelper( T * ptr ) :
|
||||
itsPtr( static_cast<ParentType *>( ptr ) ),
|
||||
itsState()
|
||||
{
|
||||
@@ -125,7 +130,7 @@ namespace cereal
|
||||
}
|
||||
|
||||
//! Restores the state of the held pointer
|
||||
inline void restore()
|
||||
inline ~EnableSharedStateHelper()
|
||||
{
|
||||
std::memcpy( itsPtr, &itsState, sizeof(ParentType) );
|
||||
}
|
||||
@@ -133,7 +138,7 @@ namespace cereal
|
||||
private:
|
||||
ParentType * itsPtr;
|
||||
StorageType itsState;
|
||||
}; // end EnableSharedHelper
|
||||
}; // end EnableSharedStateHelper
|
||||
|
||||
//! Performs loading and construction for a shared pointer that is derived from
|
||||
//! std::enable_shared_from_this
|
||||
@@ -144,13 +149,10 @@ namespace cereal
|
||||
void loadAndConstructSharedPtr( Archive & ar, T * ptr, std::true_type /* has_shared_from_this */ )
|
||||
{
|
||||
memory_detail::LoadAndConstructLoadWrapper<Archive, T> loadWrapper( ptr );
|
||||
memory_detail::EnableSharedHelper<T> helper( ptr );
|
||||
memory_detail::EnableSharedStateHelper<T> state( ptr );
|
||||
|
||||
// let the user perform their initialization
|
||||
ar( _CEREAL_NVP("data", loadWrapper) );
|
||||
|
||||
// restore the state of enable_shared_from_this
|
||||
helper.restore();
|
||||
}
|
||||
|
||||
//! Performs loading and construction for a shared pointer that is NOT derived from
|
||||
|
||||
Reference in New Issue
Block a user