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 cereal 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 RANDOLPH VOORHIES OR SHANE GRANT 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 // Work around MSVC not having alignof
38 #if defined(_MSC_VER) && _MSC_VER < 1900
39 #define CEREAL_ALIGNOF __alignof
40 #else // not MSVC 2013 or older
41 #define CEREAL_ALIGNOF alignof
42 #endif // end MSVC check
43 
44 namespace cereal
45 {
46  namespace memory_detail
47  {
49 
51  template<class T>
52  struct PtrWrapper
53  {
54  PtrWrapper(T && p) : ptr(std::forward<T>(p)) {}
55  T & ptr;
56 
57  PtrWrapper & operator=( PtrWrapper const & ) = delete;
58  };
59 
61 
62  template<class T> inline
63  PtrWrapper<T> make_ptr_wrapper(T && t)
64  {
65  return {std::forward<T>(t)};
66  }
67 
69 
72  template <class Archive, class T>
74  {
75  LoadAndConstructLoadWrapper( T * ptr ) :
76  construct( ptr )
77  { }
78 
80  template <class F>
81  LoadAndConstructLoadWrapper( T * ptr, F && sharedFromThisFunc ) :
82  construct( ptr, sharedFromThisFunc )
83  { }
84 
85  inline void CEREAL_SERIALIZE_FUNCTION_NAME( Archive & ar )
86  {
88  }
89 
91  };
92 
95 
132  template <class T>
134  {
135  // typedefs for parent type and storage type
136  using BaseType = typename ::cereal::traits::get_shared_from_this_base<T>::type;
137  using ParentType = std::enable_shared_from_this<BaseType>;
138  using StorageType = typename std::aligned_storage<sizeof(ParentType), CEREAL_ALIGNOF(ParentType)>::type;
139 
140  public:
142 
143  inline EnableSharedStateHelper( T * ptr ) :
144  itsPtr( static_cast<ParentType *>( ptr ) ),
145  itsState(),
146  itsRestored( false )
147  {
148  std::memcpy( &itsState, itsPtr, sizeof(ParentType) );
149  }
150 
152  inline void restore()
153  {
154  if( !itsRestored )
155  {
156  std::memcpy( 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  {
200  memory_detail::LoadAndConstructLoadWrapper<Archive, T> loadWrapper( ptr );
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.get() );
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  auto & ptr = wrapper.ptr;
282 
283  uint32_t id;
284 
285  ar( CEREAL_NVP_("id", id) );
286 
287  if( id & detail::msb_32bit )
288  {
289  // Storage type for the pointer - since we can't default construct this type,
290  // we'll allocate it using std::aligned_storage and use a custom deleter
291  using ST = typename std::aligned_storage<sizeof(T), CEREAL_ALIGNOF(T)>::type;
292 
293  // Valid flag - set to true once construction finishes
294  // This prevents us from calling the destructor on
295  // uninitialized data.
296  auto valid = std::make_shared<bool>( false );
297 
298  // Allocate our storage, which we will treat as
299  // uninitialized until initialized with placement new
300  ptr.reset( reinterpret_cast<T *>( new ST() ),
301  [=]( T * t )
302  {
303  if( *valid )
304  t->~T();
305 
306  delete reinterpret_cast<ST *>( t );
307  } );
308 
309  // Register the pointer
310  ar.registerSharedPointer( id, ptr );
311 
312  // Perform the actual loading and allocation
313  memory_detail::loadAndConstructSharedPtr( ar, ptr.get(), typename ::cereal::traits::has_shared_from_this<T>::type() );
314 
315  // Mark pointer as valid (initialized)
316  *valid = true;
317  }
318  else
319  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  auto & ptr = wrapper.ptr;
329 
330  uint32_t id;
331 
332  ar( CEREAL_NVP_("id", id) );
333 
334  if( id & detail::msb_32bit )
335  {
336  ptr.reset( detail::Construct<T, Archive>::load_andor_construct() );
337  ar.registerSharedPointer( id, ptr );
338  ar( CEREAL_NVP_("data", *ptr) );
339  }
340  else
341  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  // Storage type for the pointer - since we can't default construct this type,
378  // we'll allocate it using std::aligned_storage
379  using ST = typename std::aligned_storage<sizeof(T), CEREAL_ALIGNOF(T)>::type;
380 
381  // Allocate storage - note the ST type so that deleter is correct if
382  // an exception is thrown before we are initialized
383  std::unique_ptr<ST> stPtr( new ST() );
384 
385  // Use wrapper to enter into "data" nvp of ptr_wrapper
386  memory_detail::LoadAndConstructLoadWrapper<Archive, T> loadWrapper( reinterpret_cast<T *>( stPtr.get() ) );
387 
388  // Initialize storage
389  ar( CEREAL_NVP_("data", loadWrapper) );
390 
391  // Transfer ownership to correct unique_ptr type
392  ptr.reset( reinterpret_cast<T *>( stPtr.release() ) );
393  }
394  else
395  ptr.reset( nullptr );
396  }
397 
399 
400  template <class Archive, class T, class D> inline
401  typename std::enable_if<!traits::has_load_and_construct<T, Archive>::value, void>::type
402  CEREAL_LOAD_FUNCTION_NAME( Archive & ar, memory_detail::PtrWrapper<std::unique_ptr<T, D> &> & wrapper )
403  {
404  uint8_t isValid;
405  ar( CEREAL_NVP_("valid", isValid) );
406 
407  auto & ptr = wrapper.ptr;
408 
409  if( isValid )
410  {
411  ptr.reset( detail::Construct<T, Archive>::load_andor_construct() );
412  ar( CEREAL_NVP_( "data", *ptr ) );
413  }
414  else
415  {
416  ptr.reset( nullptr );
417  }
418  }
419 } // namespace cereal
420 
421 // automatically include polymorphic support
423 
424 #undef CEREAL_ALIGNOF
425 #endif // CEREAL_TYPES_SHARED_PTR_HPP_
#define CEREAL_NVP_(name, value)
Convenience for creating a templated NVP.
Definition: helpers.hpp:199
void restore()
Restores the state of the held pointer (can only be done once)
Definition: memory.hpp:152
Used to construct types with no default constructor.
Definition: access.hpp:162
A struct that acts as a wrapper around calling load_andor_construct.
Definition: memory.hpp:73
Support for pointers to polymorphic base classes.
EnableSharedStateHelper(T *ptr)
Saves the state of some type inheriting from enable_shared_from_this.
Definition: memory.hpp:143
Definition: access.hpp:40
A wrapper class to notify cereal that it is ok to serialize the contained pointer.
Definition: memory.hpp:52
Main cereal functionality.
~EnableSharedStateHelper()
Restores the state of the held pointer if not done previously.
Definition: memory.hpp:162
#define CEREAL_LOAD_FUNCTION_NAME
The deserialization (load) function name to search for.
Definition: macros.hpp:85
LoadAndConstructLoadWrapper(T *ptr, F &&sharedFromThisFunc)
Constructor for embedding an early call for restoring shared_from_this.
Definition: memory.hpp:81
#define CEREAL_SAVE_FUNCTION_NAME
The serialization (save) function name to search for.
Definition: macros.hpp:92
Definition: traits.hpp:1316