Skip to content

Commit

Permalink
Starting on solution for #44
Browse files Browse the repository at this point in the history
Won't have time to finish this one right now, but this solution should work well.

1) Realized there was no reason to do the deferring thing, we just register immediately after
allocation and use that pointer, so got rid of some overhead in code/time on regular shared_ptr loads.
2) For cases with no default constructor the interface will be changing slightly.  Instead of having the
user do both the allocation and initialization, the user will only be responsible for the initialization.
This works as follows:

We allocate std::aligned_storage big enough for the type and pack it into a shared_ptr for the proper type with
a custom deleter to correctly call the proper destructors.  We'll wrap up this raw pointer in a new class called
cereal::allocation (or similar) which will now be passed along with the archive as a reference to the load and
allocate function.  The user now loads up their data as before using the archive, and then instead of performing
a raw call to operator new, they pass all of their arguments to the operator() of the cereal::allocation object
which then performs a placement new into the aligned_storage (transparent to the user).  Should resolve the circular
reference problem too.
  • Loading branch information
AzothAmmo committed Jan 22, 2014
1 parent be4ec7d commit 3a92f0b
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 10 deletions.
28 changes: 28 additions & 0 deletions include/cereal/details/helpers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,34 @@ namespace cereal
// always get a version number of 0
};
} // namespace detail


//! Used to allocate types with no default constructor
/*! When serializing a type that has no default constructor, cereal
will attempt to call either the class static function load_and_allocate
or the appropriate template specialization of LoadAndAllocate. cereal
will pass that function a reference to the archive as well as a reference
to an allocate object which should be used to perform the allocation once
data has been appropriately loaded. */
template <class T>
class allocate
{
public:
allocate( T * p ) : ptr( p ) {}

template <class ... Args>
void operator()( Args && ... args )
{
::operator new (ptr) T( std::forward<Args>( args )... );
}

private:
allocate( allocate const & ) = delete;
allocate & operator=( allocate const & ) = delete;

T * ptr;
};

} // namespace cereal

#endif // CEREAL_DETAILS_HELPERS_HPP_
44 changes: 34 additions & 10 deletions include/cereal/types/memory.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,10 @@ namespace cereal
typename std::enable_if<traits::has_load_and_allocate<T, Archive>::value, void>::type
load( Archive & ar, memory_detail::PtrWrapper<std::shared_ptr<T> &> & wrapper )
{
// Storage type for the pointer - since we can't default construct this type,
// we'll allocate it using std::aligned_storage and use a custom deleter
using ST = typename std::aligned_storage<T>::type;

auto & ptr = wrapper.ptr;

uint32_t id;
Expand All @@ -159,12 +163,33 @@ namespace cereal
{
ar.preRegisterSharedPointer( id );

// Use wrapper to enter into "data" nvp
// Valid flag - set to true once construction finishes
// This prevents us from calling the destructor on
// uninitialized data.
auto valid = std::make_shared<bool>( false );

// Allocate our storage, which we will treat as
// uninitialized until initialized with placement new
ptr.reset( reinterpret_cast<Test *>( new TT() ),
[=]( T * t )
{
if( valid )
t->~Test();

delete reinterpret_cast<ST *>( t );
} );

// Register the pointer
ar.postRegisterSharedPointer( id, ptr );

// Use wrapper to enter into "data" nvp of ptr_wrapper
memory_detail::LoadAndAllocateLoadWrapper<Archive, T> loadWrapper;

// Call load and allocate
ar( loadWrapper );
ptr.reset( loadWrapper.ptr );

ar.postRegisterSharedPointer( id, ptr );
// Mark pointer as valid (initialized)
*valid = true;
}
else
{
Expand All @@ -190,20 +215,19 @@ namespace cereal

if( id & detail::msb_32bit )
{
ar.preRegisterSharedPointer( id );
//ar.preRegisterSharedPointer( id );

ptr.reset( detail::Load<T, Archive>::load_andor_allocate( ar ) );
ar( *ptr );

ar.postRegisterSharedPointer( id, ptr );
ar( *ptr );
}
else
{
if( ar.isSharedPointerValid( id ) )
//if( ar.isSharedPointerValid( id ) )
ptr = std::static_pointer_cast<T>(ar.getSharedPointer(id));
else
ar.pushDeferredSharedPointerLoad(
id, [&, id]() { ptr = std::static_pointer_cast<T>(ar.getSharedPointer(id)); } );
//else
// ar.pushDeferredSharedPointerLoad(
// id, [&, id]() { ptr = std::static_pointer_cast<T>(ar.getSharedPointer(id)); } );
}
}

Expand Down

0 comments on commit 3a92f0b

Please sign in to comment.