cereal
A C++11 library for serialization
memory.hpp
Go to the documentation of this file.
1 
4 /*
5  Copyright (c) 2014, Randolph Voorhies, Shane Grant
6  All rights reserved.
7 
8  Redistribution and use in source and binary forms, with or without
9  modification, are permitted provided that the following conditions are met:
10  * Redistributions of source code must retain the above copyright
11  notice, this list of conditions and the following disclaimer.
12  * Redistributions in binary form must reproduce the above copyright
13  notice, this list of conditions and the following disclaimer in the
14  documentation and/or other materials provided with the distribution.
15  * Neither the name of the copyright holder nor the
16  names of its contributors may be used to endorse or promote products
17  derived from this software without specific prior written permission.
18 
19  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
23  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30 #ifndef CEREAL_TYPES_SHARED_PTR_HPP_
31 #define CEREAL_TYPES_SHARED_PTR_HPP_
32 
33 #include "cereal/cereal.hpp"
34 #include <memory>
35 #include <cstring>
36 
37 namespace cereal
38 {
39  namespace memory_detail
40  {
42 
44  template<class T>
45  struct PtrWrapper
46  {
47  PtrWrapper(T && p) : ptr(std::forward<T>(p)) {}
48  T & ptr;
49 
50  PtrWrapper( PtrWrapper const & ) = default;
51  PtrWrapper & operator=( PtrWrapper const & ) = delete;
52  };
53 
55 
56  template<class T> inline
58  {
59  return {std::forward<T>(t)};
60  }
61 
63 
66  template <class Archive, class T>
68  {
69  LoadAndConstructLoadWrapper( T * ptr ) :
70  construct( ptr )
71  { }
72 
74  template <class F>
75  LoadAndConstructLoadWrapper( T * ptr, F && sharedFromThisFunc ) :
76  construct( ptr, sharedFromThisFunc )
77  { }
78 
79  inline void CEREAL_SERIALIZE_FUNCTION_NAME( Archive & ar )
80  {
82  }
83 
85  };
86 
89 
131  template <class T>
133  {
134  // typedefs for parent type and storage type
135  using BaseType = typename ::cereal::traits::get_shared_from_this_base<T>::type;
136  using ParentType = std::enable_shared_from_this<BaseType>;
137  using StorageType = typename std::aligned_storage<sizeof(ParentType), CEREAL_ALIGNOF(ParentType)>::type;
138 
139  public:
141 
142  inline EnableSharedStateHelper( T * ptr ) :
143  itsPtr( static_cast<ParentType *>( ptr ) ),
144  itsState(),
145  itsRestored( false )
146  {
147  std::memcpy( &itsState, itsPtr, sizeof(ParentType) );
148  }
149 
151  inline void restore()
152  {
153  if( !itsRestored )
154  {
155  // void * cast needed when type has no trivial copy-assignment
156  std::memcpy( static_cast<void *>(itsPtr), &itsState, sizeof(ParentType) );
157  itsRestored = true;
158  }
159  }
160 
163  {
164  restore();
165  }
166 
167  private:
168  ParentType * itsPtr;
169  StorageType itsState;
170  bool itsRestored;
171  }; // end EnableSharedStateHelper
172 
175 
178  template <class Archive, class T> inline
179  void loadAndConstructSharedPtr( Archive & ar, T * ptr, std::true_type /* has_shared_from_this */ )
180  {
182  memory_detail::LoadAndConstructLoadWrapper<Archive, T> loadWrapper( ptr, [&](){ state.restore(); } );
183 
184  // let the user perform their initialization, shared state will be restored as soon as construct()
185  // is called
186  ar( CEREAL_NVP_("data", loadWrapper) );
187  }
188 
191 
197  template <class Archive, class T> inline
198  void loadAndConstructSharedPtr( Archive & ar, T * ptr, std::false_type /* has_shared_from_this */ )
199  {
201  ar( CEREAL_NVP_("data", loadWrapper) );
202  }
203  } // end namespace memory_detail
204 
206  template <class Archive, class T> inline
207  typename std::enable_if<!std::is_polymorphic<T>::value, void>::type
208  CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::shared_ptr<T> const & ptr )
209  {
210  ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper( ptr )) );
211  }
212 
214  template <class Archive, class T> inline
215  typename std::enable_if<!std::is_polymorphic<T>::value, void>::type
216  CEREAL_LOAD_FUNCTION_NAME( Archive & ar, std::shared_ptr<T> & ptr )
217  {
218  ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper( ptr )) );
219  }
220 
222  template <class Archive, class T> inline
223  typename std::enable_if<!std::is_polymorphic<T>::value, void>::type
224  CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::weak_ptr<T> const & ptr )
225  {
226  auto const sptr = ptr.lock();
227  ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper( sptr )) );
228  }
229 
231  template <class Archive, class T> inline
232  typename std::enable_if<!std::is_polymorphic<T>::value, void>::type
233  CEREAL_LOAD_FUNCTION_NAME( Archive & ar, std::weak_ptr<T> & ptr )
234  {
235  std::shared_ptr<T> sptr;
236  ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper( sptr )) );
237  ptr = sptr;
238  }
239 
241  template <class Archive, class T, class D> inline
242  typename std::enable_if<!std::is_polymorphic<T>::value, void>::type
243  CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::unique_ptr<T, D> const & ptr )
244  {
245  ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper( ptr )) );
246  }
247 
249  template <class Archive, class T, class D> inline
250  typename std::enable_if<!std::is_polymorphic<T>::value, void>::type
251  CEREAL_LOAD_FUNCTION_NAME( Archive & ar, std::unique_ptr<T, D> & ptr )
252  {
253  ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper( ptr )) );
254  }
255 
256  // ######################################################################
257  // Pointer wrapper implementations follow below
258 
260 
261  template <class Archive, class T> inline
262  void CEREAL_SAVE_FUNCTION_NAME( Archive & ar, memory_detail::PtrWrapper<std::shared_ptr<T> const &> const & wrapper )
263  {
264  auto & ptr = wrapper.ptr;
265 
266  uint32_t id = ar.registerSharedPointer( ptr );
267  ar( CEREAL_NVP_("id", id) );
268 
269  if( id & detail::msb_32bit )
270  {
271  ar( CEREAL_NVP_("data", *ptr) );
272  }
273  }
274 
276 
277  template <class Archive, class T> inline
278  typename std::enable_if<traits::has_load_and_construct<T, Archive>::value, void>::type
279  CEREAL_LOAD_FUNCTION_NAME( Archive & ar, memory_detail::PtrWrapper<std::shared_ptr<T> &> & wrapper )
280  {
281  uint32_t id;
282 
283  ar( CEREAL_NVP_("id", id) );
284 
285  if( id & detail::msb_32bit )
286  {
287  // Storage type for the pointer - since we can't default construct this type,
288  // we'll allocate it using std::aligned_storage and use a custom deleter
289  using ST = typename std::aligned_storage<sizeof(T), CEREAL_ALIGNOF(T)>::type;
290 
291  // Valid flag - set to true once construction finishes
292  // This prevents us from calling the destructor on
293  // uninitialized data.
294  auto valid = std::make_shared<bool>( false );
295 
296  // Allocate our storage, which we will treat as
297  // uninitialized until initialized with placement new
298  using NonConstT = typename std::remove_const<T>::type;
299  std::shared_ptr<NonConstT> ptr(reinterpret_cast<NonConstT *>(new ST()),
300  [=]( NonConstT * t )
301  {
302  if( *valid )
303  t->~T();
304 
305  delete reinterpret_cast<ST *>( t );
306  } );
307 
308  // Register the pointer
309  ar.registerSharedPointer( id, ptr );
310 
311  // Perform the actual loading and allocation
312  memory_detail::loadAndConstructSharedPtr( ar, ptr.get(), typename ::cereal::traits::has_shared_from_this<NonConstT>::type() );
313 
314  // Mark pointer as valid (initialized)
315  *valid = true;
316  wrapper.ptr = std::move(ptr);
317  }
318  else
319  wrapper.ptr = std::static_pointer_cast<T>(ar.getSharedPointer(id));
320  }
321 
323 
324  template <class Archive, class T> inline
325  typename std::enable_if<!traits::has_load_and_construct<T, Archive>::value, void>::type
326  CEREAL_LOAD_FUNCTION_NAME( Archive & ar, memory_detail::PtrWrapper<std::shared_ptr<T> &> & wrapper )
327  {
328  uint32_t id;
329 
330  ar( CEREAL_NVP_("id", id) );
331 
332  if( id & detail::msb_32bit )
333  {
334  using NonConstT = typename std::remove_const<T>::type;
335  std::shared_ptr<NonConstT> ptr( detail::Construct<NonConstT, Archive>::load_andor_construct() );
336  ar.registerSharedPointer( id, ptr );
337  ar( CEREAL_NVP_("data", *ptr) );
338  wrapper.ptr = std::move(ptr);
339  }
340  else
341  wrapper.ptr = std::static_pointer_cast<T>(ar.getSharedPointer(id));
342  }
343 
345 
346  template <class Archive, class T, class D> inline
347  void CEREAL_SAVE_FUNCTION_NAME( Archive & ar, memory_detail::PtrWrapper<std::unique_ptr<T, D> const &> const & wrapper )
348  {
349  auto & ptr = wrapper.ptr;
350 
351  // unique_ptr get one byte of metadata which signifies whether they were a nullptr
352  // 0 == nullptr
353  // 1 == not null
354 
355  if( !ptr )
356  ar( CEREAL_NVP_("valid", uint8_t(0)) );
357  else
358  {
359  ar( CEREAL_NVP_("valid", uint8_t(1)) );
360  ar( CEREAL_NVP_("data", *ptr) );
361  }
362  }
363 
365 
366  template <class Archive, class T, class D> inline
367  typename std::enable_if<traits::has_load_and_construct<T, Archive>::value, void>::type
368  CEREAL_LOAD_FUNCTION_NAME( Archive & ar, memory_detail::PtrWrapper<std::unique_ptr<T, D> &> & wrapper )
369  {
370  uint8_t isValid;
371  ar( CEREAL_NVP_("valid", isValid) );
372 
373  auto & ptr = wrapper.ptr;
374 
375  if( isValid )
376  {
377  using NonConstT = typename std::remove_const<T>::type;
378  // Storage type for the pointer - since we can't default construct this type,
379  // we'll allocate it using std::aligned_storage
380  using ST = typename std::aligned_storage<sizeof(NonConstT), CEREAL_ALIGNOF(NonConstT)>::type;
381 
382  // Allocate storage - note the ST type so that deleter is correct if
383  // an exception is thrown before we are initialized
384  std::unique_ptr<ST> stPtr( new ST() );
385 
386  // Use wrapper to enter into "data" nvp of ptr_wrapper
387  memory_detail::LoadAndConstructLoadWrapper<Archive, NonConstT> loadWrapper( reinterpret_cast<NonConstT *>( stPtr.get() ) );
388 
389  // Initialize storage
390  ar( CEREAL_NVP_("data", loadWrapper) );
391 
392  // Transfer ownership to correct unique_ptr type
393  ptr.reset( reinterpret_cast<T *>( stPtr.release() ) );
394  }
395  else
396  ptr.reset( nullptr );
397  }
398 
400 
401  template <class Archive, class T, class D> inline
402  typename std::enable_if<!traits::has_load_and_construct<T, Archive>::value, void>::type
403  CEREAL_LOAD_FUNCTION_NAME( Archive & ar, memory_detail::PtrWrapper<std::unique_ptr<T, D> &> & wrapper )
404  {
405  uint8_t isValid;
406  ar( CEREAL_NVP_("valid", isValid) );
407 
408  if( isValid )
409  {
410  using NonConstT = typename std::remove_const<T>::type;
411  std::unique_ptr<NonConstT, D> ptr( detail::Construct<NonConstT, Archive>::load_andor_construct() );
412  ar( CEREAL_NVP_( "data", *ptr ) );
413  wrapper.ptr = std::move(ptr);
414  }
415  else
416  {
417  wrapper.ptr.reset( nullptr );
418  }
419  }
420 } // namespace cereal
421 
422 // automatically include polymorphic support
424 
425 #endif // CEREAL_TYPES_SHARED_PTR_HPP_
CEREAL_NVP_
#define CEREAL_NVP_(name, value)
Convenience for creating a templated NVP.
Definition: helpers.hpp:201
cereal::memory_detail::LoadAndConstructLoadWrapper::LoadAndConstructLoadWrapper
LoadAndConstructLoadWrapper(T *ptr, F &&sharedFromThisFunc)
Constructor for embedding an early call for restoring shared_from_this.
Definition: memory.hpp:75
cereal::construct
Used to construct types with no default constructor.
Definition: access.hpp:164
cereal::memory_detail::loadAndConstructSharedPtr
void loadAndConstructSharedPtr(Archive &ar, T *ptr, std::true_type)
Definition: memory.hpp:179
CEREAL_ALIGNOF
#define CEREAL_ALIGNOF
Checks if C++14 is available.
Definition: macros.hpp:153
cereal::memory_detail::EnableSharedStateHelper::restore
void restore()
Restores the state of the held pointer (can only be done once)
Definition: memory.hpp:151
CEREAL_LOAD_FUNCTION_NAME
#define CEREAL_LOAD_FUNCTION_NAME
The deserialization (load) function name to search for.
Definition: macros.hpp:85
cereal::detail::Construct
Definition: traits.hpp:1338
cereal::memory_detail::EnableSharedStateHelper::EnableSharedStateHelper
EnableSharedStateHelper(T *ptr)
Saves the state of some type inheriting from enable_shared_from_this.
Definition: memory.hpp:142
cereal::memory_detail::PtrWrapper
A wrapper class to notify cereal that it is ok to serialize the contained pointer.
Definition: memory.hpp:45
CEREAL_SAVE_FUNCTION_NAME
#define CEREAL_SAVE_FUNCTION_NAME
The serialization (save) function name to search for.
Definition: macros.hpp:92
cereal::memory_detail::EnableSharedStateHelper
Definition: memory.hpp:132
cereal::memory_detail::EnableSharedStateHelper::~EnableSharedStateHelper
~EnableSharedStateHelper()
Restores the state of the held pointer if not done previously.
Definition: memory.hpp:162
cereal::memory_detail::make_ptr_wrapper
PtrWrapper< T > make_ptr_wrapper(T &&t)
Make a PtrWrapper.
Definition: memory.hpp:57
polymorphic.hpp
Support for pointers to polymorphic base classes.
cereal.hpp
Main cereal functionality.
cereal::memory_detail::LoadAndConstructLoadWrapper
A struct that acts as a wrapper around calling load_andor_construct.
Definition: memory.hpp:67