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 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 & operator=( PtrWrapper const & ) = delete;
51  };
52 
54 
55  template<class T> inline
57  {
58  return {std::forward<T>(t)};
59  }
60 
62 
65  template <class Archive, class T>
67  {
68  LoadAndConstructLoadWrapper( T * ptr ) :
69  construct( ptr )
70  { }
71 
72  inline void CEREAL_SERIALIZE_FUNCTION_NAME( Archive & ar )
73  {
75  }
76 
78  };
79 
82 
116  template <class T>
118  {
119  // typedefs for parent type and storage type
120  using BaseType = typename ::cereal::traits::get_shared_from_this_base<T>::type;
121  using ParentType = std::enable_shared_from_this<BaseType>;
122  using StorageType = typename std::aligned_storage<sizeof(ParentType)>::type;
123 
124  public:
126 
127  inline EnableSharedStateHelper( T * ptr ) :
128  itsPtr( static_cast<ParentType *>( ptr ) ),
129  itsState()
130  {
131  std::memcpy( &itsState, itsPtr, sizeof(ParentType) );
132  }
133 
136  {
137  std::memcpy( itsPtr, &itsState, sizeof(ParentType) );
138  }
139 
140  private:
141  ParentType * itsPtr;
142  StorageType itsState;
143  }; // end EnableSharedStateHelper
144 
147 
150  template <class Archive, class T> inline
151  void loadAndConstructSharedPtr( Archive & ar, T * ptr, std::true_type /* has_shared_from_this */ )
152  {
155 
156  // let the user perform their initialization
157  ar( CEREAL_NVP_("data", loadWrapper) );
158  }
159 
162 
168  template <class Archive, class T> inline
169  void loadAndConstructSharedPtr( Archive & ar, T * ptr, std::false_type /* has_shared_from_this */ )
170  {
172  ar( CEREAL_NVP_("data", loadWrapper) );
173  }
174  } // end namespace memory_detail
175 
177  template <class Archive, class T> inline
178  typename std::enable_if<!std::is_polymorphic<T>::value, void>::type
179  CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::shared_ptr<T> const & ptr )
180  {
181  ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper( ptr )) );
182  }
183 
185  template <class Archive, class T> inline
186  typename std::enable_if<!std::is_polymorphic<T>::value, void>::type
187  CEREAL_LOAD_FUNCTION_NAME( Archive & ar, std::shared_ptr<T> & ptr )
188  {
189  ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper( ptr )) );
190  }
191 
193  template <class Archive, class T> inline
194  typename std::enable_if<!std::is_polymorphic<T>::value, void>::type
195  CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::weak_ptr<T> const & ptr )
196  {
197  auto const sptr = ptr.lock();
198  ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper( sptr )) );
199  }
200 
202  template <class Archive, class T> inline
203  typename std::enable_if<!std::is_polymorphic<T>::value, void>::type
204  CEREAL_LOAD_FUNCTION_NAME( Archive & ar, std::weak_ptr<T> & ptr )
205  {
206  std::shared_ptr<T> sptr;
207  ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper( sptr )) );
208  ptr = sptr;
209  }
210 
212  template <class Archive, class T, class D> inline
213  typename std::enable_if<!std::is_polymorphic<T>::value, void>::type
214  CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::unique_ptr<T, D> const & ptr )
215  {
216  ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper( ptr )) );
217  }
218 
220  template <class Archive, class T, class D> inline
221  typename std::enable_if<!std::is_polymorphic<T>::value, void>::type
222  CEREAL_LOAD_FUNCTION_NAME( Archive & ar, std::unique_ptr<T, D> & ptr )
223  {
224  ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper( ptr )) );
225  }
226 
227  // ######################################################################
228  // Pointer wrapper implementations follow below
229 
231 
232  template <class Archive, class T> inline
233  void CEREAL_SAVE_FUNCTION_NAME( Archive & ar, memory_detail::PtrWrapper<std::shared_ptr<T> const &> const & wrapper )
234  {
235  auto & ptr = wrapper.ptr;
236 
237  uint32_t id = ar.registerSharedPointer( ptr.get() );
238  ar( CEREAL_NVP_("id", id) );
239 
240  if( id & detail::msb_32bit )
241  {
242  ar( CEREAL_NVP_("data", *ptr) );
243  }
244  }
245 
247 
248  template <class Archive, class T> inline
249  typename std::enable_if<traits::has_load_and_construct<T, Archive>::value, void>::type
250  CEREAL_LOAD_FUNCTION_NAME( Archive & ar, memory_detail::PtrWrapper<std::shared_ptr<T> &> & wrapper )
251  {
252  auto & ptr = wrapper.ptr;
253 
254  uint32_t id;
255 
256  ar( CEREAL_NVP_("id", id) );
257 
258  if( id & detail::msb_32bit )
259  {
260  // Storage type for the pointer - since we can't default construct this type,
261  // we'll allocate it using std::aligned_storage and use a custom deleter
262  using ST = typename std::aligned_storage<sizeof(T)>::type;
263 
264  // Valid flag - set to true once construction finishes
265  // This prevents us from calling the destructor on
266  // uninitialized data.
267  auto valid = std::make_shared<bool>( false );
268 
269  // Allocate our storage, which we will treat as
270  // uninitialized until initialized with placement new
271  ptr.reset( reinterpret_cast<T *>( new ST() ),
272  [=]( T * t )
273  {
274  if( valid )
275  t->~T();
276 
277  delete reinterpret_cast<ST *>( t );
278  } );
279 
280  // Register the pointer
281  ar.registerSharedPointer( id, ptr );
282 
283  // Perform the actual loading and allocation
284  memory_detail::loadAndConstructSharedPtr( ar, ptr.get(), typename ::cereal::traits::has_shared_from_this<T>::type() );
285 
286  // Mark pointer as valid (initialized)
287  *valid = true;
288  }
289  else
290  ptr = std::static_pointer_cast<T>(ar.getSharedPointer(id));
291  }
292 
294 
295  template <class Archive, class T> inline
296  typename std::enable_if<!traits::has_load_and_construct<T, Archive>::value, void>::type
297  CEREAL_LOAD_FUNCTION_NAME( Archive & ar, memory_detail::PtrWrapper<std::shared_ptr<T> &> & wrapper )
298  {
299  auto & ptr = wrapper.ptr;
300 
301  uint32_t id;
302 
303  ar( CEREAL_NVP_("id", id) );
304 
305  if( id & detail::msb_32bit )
306  {
308  ar.registerSharedPointer( id, ptr );
309  ar( CEREAL_NVP_("data", *ptr) );
310  }
311  else
312  ptr = std::static_pointer_cast<T>(ar.getSharedPointer(id));
313  }
314 
316 
317  template <class Archive, class T, class D> inline
318  void CEREAL_SAVE_FUNCTION_NAME( Archive & ar, memory_detail::PtrWrapper<std::unique_ptr<T, D> const &> const & wrapper )
319  {
320  auto & ptr = wrapper.ptr;
321 
322  // unique_ptr get one byte of metadata which signifies whether they were a nullptr
323  // 0 == nullptr
324  // 1 == not null
325 
326  if( !ptr )
327  ar( CEREAL_NVP_("valid", uint8_t(0)) );
328  else
329  {
330  ar( CEREAL_NVP_("valid", uint8_t(1)) );
331  ar( CEREAL_NVP_("data", *ptr) );
332  }
333  }
334 
336 
337  template <class Archive, class T, class D> inline
338  typename std::enable_if<traits::has_load_and_construct<T, Archive>::value, void>::type
339  CEREAL_LOAD_FUNCTION_NAME( Archive & ar, memory_detail::PtrWrapper<std::unique_ptr<T, D> &> & wrapper )
340  {
341  uint8_t isValid;
342  ar( CEREAL_NVP_("valid", isValid) );
343 
344  auto & ptr = wrapper.ptr;
345 
346  if( isValid )
347  {
348  // Storage type for the pointer - since we can't default construct this type,
349  // we'll allocate it using std::aligned_storage
350  using ST = typename std::aligned_storage<sizeof(T)>::type;
351 
352  // Allocate storage - note the ST type so that deleter is correct if
353  // an exception is thrown before we are initialized
354  std::unique_ptr<ST> stPtr( new ST() );
355 
356  // Use wrapper to enter into "data" nvp of ptr_wrapper
357  memory_detail::LoadAndConstructLoadWrapper<Archive, T> loadWrapper( reinterpret_cast<T *>( stPtr.get() ) );
358 
359  // Initialize storage
360  ar( CEREAL_NVP_("data", loadWrapper) );
361 
362  // Transfer ownership to correct unique_ptr type
363  ptr.reset( reinterpret_cast<T *>( stPtr.release() ) );
364  }
365  else
366  ptr.reset( nullptr );
367  }
368 
370 
371  template <class Archive, class T, class D> inline
372  typename std::enable_if<!traits::has_load_and_construct<T, Archive>::value, void>::type
373  CEREAL_LOAD_FUNCTION_NAME( Archive & ar, memory_detail::PtrWrapper<std::unique_ptr<T, D> &> & wrapper )
374  {
375  uint8_t isValid;
376  ar( CEREAL_NVP_("valid", isValid) );
377 
378  auto & ptr = wrapper.ptr;
379 
380  if( isValid )
381  {
383  ar( CEREAL_NVP_( "data", *ptr ) );
384  }
385  else
386  {
387  ptr.reset( nullptr );
388  }
389  }
390 } // namespace cereal
391 
392 #endif // CEREAL_TYPES_SHARED_PTR_HPP_
#define CEREAL_NVP_(name, value)
Convenience for creating a templated NVP.
Definition: helpers.hpp:197
void loadAndConstructSharedPtr(Archive &ar, T *ptr, std::true_type)
Definition: memory.hpp:151
Used to construct types with no default constructor.
Definition: access.hpp:151
#define CEREAL_SERIALIZE_FUNCTION_NAME
The serialization/deserialization function name to search for.
Definition: macros.hpp:51
A struct that acts as a wrapper around calling load_andor_construct.
Definition: memory.hpp:66
EnableSharedStateHelper(T *ptr)
Saves the state of some type inheriting from enable_shared_from_this.
Definition: memory.hpp:127
Definition: access.hpp:39
A wrapper class to notify cereal that it is ok to serialize the contained pointer.
Definition: memory.hpp:45
Main cereal functionality.
~EnableSharedStateHelper()
Restores the state of the held pointer.
Definition: memory.hpp:135
#define CEREAL_LOAD_FUNCTION_NAME
The deserialization (load) function name to search for.
Definition: macros.hpp:58
PtrWrapper< T > make_ptr_wrapper(T &&t)
Make a PtrWrapper.
Definition: memory.hpp:56
#define CEREAL_SAVE_FUNCTION_NAME
The serialization (save) function name to search for.
Definition: macros.hpp:65
Definition: traits.hpp:1279