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:
Shane Grant
2014-03-13 11:53:24 -07:00
parent b7fd587b9e
commit 2a0510506c
2 changed files with 55 additions and 32 deletions
+42 -21
View File
@@ -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
+13 -11
View File
@@ -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