mirror of
https://github.com/USCiLab/cereal.git
synced 2025-09-22 21:09:33 +02:00
Support for std::enable_shared_from_this
Fixes #47 When we detect a shared_ptr being loaded in load_and_allocate that also is of a type that derives from enable_shared_from_this, extra work is done to save the state of the enable_shared_from_this before the user gets to meddle with it via placement new inside of cereal::allocate. State is restored after getting back from the user.
This commit is contained in:
parent
6c163a54eb
commit
29578c8c48
@ -29,7 +29,6 @@
|
||||
#ifndef CEREAL_CEREAL_HPP_
|
||||
#define CEREAL_CEREAL_HPP_
|
||||
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
@ -35,6 +35,7 @@
|
||||
#include <utility>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <cereal/details/static_object.hpp>
|
||||
|
||||
|
@ -32,6 +32,7 @@
|
||||
|
||||
#include <cereal/cereal.hpp>
|
||||
#include <memory>
|
||||
#include <cstring>
|
||||
|
||||
namespace cereal
|
||||
{
|
||||
@ -73,6 +74,64 @@ namespace cereal
|
||||
|
||||
::cereal::allocate<T> allocate;
|
||||
};
|
||||
|
||||
//! Performs loading and allocation for a shared pointer that is NOT derived from
|
||||
//! std::enable_shared_from_this
|
||||
/*! This is the typical case, where we simply pass the load wrapper to the
|
||||
archive
|
||||
|
||||
@param ar The archive
|
||||
@param ptr Raw pointer held by the shared_ptr
|
||||
@internal */
|
||||
template <class Archive, class T> inline
|
||||
void loadAndAllocateSharedPtr( Archive & ar, T * ptr, std::false_type /* is_base_of<enable_shared...> */ )
|
||||
{
|
||||
memory_detail::LoadAndAllocateLoadWrapper<Archive, T> loadWrapper( ptr );
|
||||
ar( loadWrapper );
|
||||
}
|
||||
|
||||
//! Performs loading and allocation for a shared pointer that is derived from
|
||||
//! std::enable_shared_from_this
|
||||
/*! This special case is necessary because when a user uses load_and_allocate,
|
||||
the weak_ptr (or whatever implementation defined variant) that allows
|
||||
enable_shared_from_this to function correctly will not be initialized properly.
|
||||
|
||||
This happens because it is the allocation of shared_ptr that perform this
|
||||
initialization, which we let happen on a buffer of memory (aligned_storage).
|
||||
This buffer is then used for placement new later on, effectively overwriting
|
||||
any initialized weak_ptr with a default initialized one, eventually leading
|
||||
to issues when the user calls shared_from_this.
|
||||
|
||||
To get around these issues, we will store the memory for the enable_shared_from_this
|
||||
portion of the class and replace it after the user performs initialization
|
||||
(placement new).
|
||||
|
||||
@param ar The archive
|
||||
@param ptr Raw pointer held by the shared_ptr
|
||||
@internal */
|
||||
template <class Archive, class T> inline
|
||||
void loadAndAllocateSharedPtr( Archive & ar, T * ptr, std::true_type /* is_base_of<enable_shared...> */ )
|
||||
{
|
||||
memory_detail::LoadAndAllocateLoadWrapper<Archive, T> loadWrapper( ptr );
|
||||
|
||||
// typedefs for parent type and storage type
|
||||
using PT = std::enable_shared_from_this<T>;
|
||||
using ST = typename std::aligned_storage<sizeof(PT)>::type;
|
||||
|
||||
// Buffer to store the current enable_shared_from_this data
|
||||
ST temp;
|
||||
|
||||
// For some reason GCC can't seem to handle the static_cast directly
|
||||
// in the call to memcpy, thus the need for ptrAsPT
|
||||
auto const ptrAsPT = static_cast<PT *>( ptr );
|
||||
std::memcpy( &temp, ptrAsPT, sizeof(PT) );
|
||||
|
||||
// let the user perform their initialization
|
||||
ar( loadWrapper );
|
||||
|
||||
// restore the state of enable_shared_from_this
|
||||
std::memcpy( ptrAsPT, &temp, sizeof(PT) );
|
||||
}
|
||||
}
|
||||
|
||||
//! Saving std::shared_ptr for non polymorphic types
|
||||
@ -182,11 +241,9 @@ namespace cereal
|
||||
// Register the pointer
|
||||
ar.registerSharedPointer( id, ptr );
|
||||
|
||||
// Use wrapper to enter into "data" nvp of ptr_wrapper
|
||||
memory_detail::LoadAndAllocateLoadWrapper<Archive, T> loadWrapper( ptr.get() );
|
||||
|
||||
// Call load and allocate
|
||||
ar( loadWrapper );
|
||||
// Perform the actual loading and allocation
|
||||
memory_detail::loadAndAllocateSharedPtr( ar, ptr.get(),
|
||||
typename std::is_base_of<std::enable_shared_from_this<T>, T>::type() );
|
||||
|
||||
// Mark pointer as valid (initialized)
|
||||
*valid = true;
|
||||
|
@ -322,14 +322,12 @@ enum Bla
|
||||
template <class Archive>
|
||||
void save( Archive & ar, Bla const & b )
|
||||
{
|
||||
std::cerr << "save" << std::endl;
|
||||
ar( (const int &)b );
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
void load( Archive & ar, Bla & b )
|
||||
{
|
||||
std::cerr << "load" << std::endl;
|
||||
ar( (int&)b );
|
||||
}
|
||||
|
||||
|
@ -1209,6 +1209,34 @@ namespace cereal
|
||||
};
|
||||
}
|
||||
|
||||
struct ThreeLA : std::enable_shared_from_this<ThreeLA>
|
||||
{
|
||||
ThreeLA( int xx ) : x( xx ) {}
|
||||
|
||||
int x;
|
||||
|
||||
template <class Archive>
|
||||
void serialize( Archive & ar )
|
||||
{ ar( x ); }
|
||||
|
||||
bool operator==( ThreeLA const & other ) const
|
||||
{ return x == other.x; }
|
||||
|
||||
template <class Archive>
|
||||
static void load_and_allocate( Archive & ar, cereal::allocate<ThreeLA> & allocate )
|
||||
{
|
||||
int xx;
|
||||
ar( xx );
|
||||
allocate( xx );
|
||||
}
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, ThreeLA const & s)
|
||||
{
|
||||
os << "[" << s.x << "]";
|
||||
return os;
|
||||
}
|
||||
|
||||
template <class IArchive, class OArchive>
|
||||
void test_memory_load_allocate()
|
||||
{
|
||||
@ -1221,6 +1249,7 @@ void test_memory_load_allocate()
|
||||
auto o_shared2 = std::make_shared<TwoLA>( random_value<int>(gen) );
|
||||
std::unique_ptr<OneLA> o_unique1( new OneLA( random_value<int>(gen) ) );
|
||||
std::unique_ptr<TwoLA> o_unique2( new TwoLA( random_value<int>(gen) ) );
|
||||
auto o_shared3 = std::make_shared<ThreeLA>( random_value<int>(gen) );
|
||||
|
||||
std::ostringstream os;
|
||||
{
|
||||
@ -1230,12 +1259,14 @@ void test_memory_load_allocate()
|
||||
oar( o_shared2 );
|
||||
oar( o_unique1 );
|
||||
oar( o_unique2 );
|
||||
oar( o_shared3 );
|
||||
}
|
||||
|
||||
decltype(o_shared1) i_shared1;
|
||||
decltype(o_shared2) i_shared2;
|
||||
decltype(o_unique1) i_unique1;
|
||||
decltype(o_unique2) i_unique2;
|
||||
decltype(o_shared3) i_shared3;
|
||||
|
||||
std::istringstream is(os.str());
|
||||
{
|
||||
@ -1245,12 +1276,17 @@ void test_memory_load_allocate()
|
||||
iar( i_shared2 );
|
||||
iar( i_unique1 );
|
||||
iar( i_unique2 );
|
||||
iar( i_shared3 );
|
||||
}
|
||||
|
||||
BOOST_CHECK_EQUAL( *o_shared1, *i_shared1 );
|
||||
BOOST_CHECK_EQUAL( *o_shared2, *i_shared2 );
|
||||
BOOST_CHECK_EQUAL( *o_unique1, *i_unique1 );
|
||||
BOOST_CHECK_EQUAL( *o_unique2, *i_unique2 );
|
||||
BOOST_CHECK_EQUAL( *o_shared3, *i_shared3 );
|
||||
|
||||
auto i_shared3_2 = i_shared3->shared_from_this();
|
||||
BOOST_CHECK_EQUAL( *o_shared3, *i_shared3_2 );
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user