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
37namespace cereal
38{
39 namespace memory_detail
40 {
42
44 template<class T>
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 {
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_
Main cereal functionality.
Used to construct types with no default constructor.
Definition: access.hpp:165
~EnableSharedStateHelper()
Restores the state of the held pointer if not done previously.
Definition: memory.hpp:162
EnableSharedStateHelper(T *ptr)
Saves the state of some type inheriting from enable_shared_from_this.
Definition: memory.hpp:142
void restore()
Restores the state of the held pointer (can only be done once)
Definition: memory.hpp:151
#define CEREAL_NVP_(name, value)
Convenience for creating a templated NVP.
Definition: helpers.hpp:201
#define CEREAL_LOAD_FUNCTION_NAME
The deserialization (load) function name to search for.
Definition: macros.hpp:85
#define CEREAL_SAVE_FUNCTION_NAME
The serialization (save) function name to search for.
Definition: macros.hpp:92
#define CEREAL_ALIGNOF
Checks if C++14 is available.
Definition: macros.hpp:153
void loadAndConstructSharedPtr(Archive &ar, T *ptr, std::true_type)
Definition: memory.hpp:179
PtrWrapper< T > make_ptr_wrapper(T &&t)
Make a PtrWrapper.
Definition: memory.hpp:57
Support for pointers to polymorphic base classes.
Definition: traits.hpp:1339
A struct that acts as a wrapper around calling load_andor_construct.
Definition: memory.hpp:68
LoadAndConstructLoadWrapper(T *ptr, F &&sharedFromThisFunc)
Constructor for embedding an early call for restoring shared_from_this.
Definition: memory.hpp:75
A wrapper class to notify cereal that it is ok to serialize the contained pointer.
Definition: memory.hpp:46