LCOV - code coverage report
Current view: top level - cereal/types - memory.hpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 2620 2703 96.9 %
Date: 2019-10-22 20:09:23 Functions: 666 706 94.3 %

          Line data    Source code
       1             : /*! \file memory.hpp
       2             :     \brief Support for types found in \<memory\>
       3             :     \ingroup STLSupport */
       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             :   {
      41             :     //! A wrapper class to notify cereal that it is ok to serialize the contained pointer
      42             :     /*! This mechanism allows us to intercept and properly handle polymorphic pointers
      43             :         @internal */
      44             :     template<class T>
      45             :     struct PtrWrapper
      46             :     {
      47      274286 :       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             : 
      54      277212 :     //! Make a PtrWrapper
      55             :     /*! @internal */
      56             :     template<class T> inline
      57      274286 :     PtrWrapper<T> make_ptr_wrapper(T && t)
      58             :     {
      59      274286 :       return {std::forward<T>(t)};
      60             :     }
      61       83735 : 
      62             :     //! A struct that acts as a wrapper around calling load_andor_construct
      63       83735 :     /*! The purpose of this is to allow a load_and_construct call to properly enter into the
      64      277212 :         'data' NVP of the ptr_wrapper
      65       83335 :         @internal */
      66      277212 :     template <class Archive, class T>
      67       83335 :     struct LoadAndConstructLoadWrapper
      68       85198 :     {
      69       42800 :       LoadAndConstructLoadWrapper( T * ptr ) :
      70       85198 :         construct( ptr )
      71       42800 :       { }
      72       84798 : 
      73       43600 :       //! Constructor for embedding an early call for restoring shared_from_this
      74       84798 :       template <class F>
      75       43600 :       LoadAndConstructLoadWrapper( T * ptr, F && sharedFromThisFunc ) :
      76       42800 :         construct( ptr, sharedFromThisFunc )
      77        3600 :       { }
      78       43600 : 
      79        3600 :       inline void CEREAL_SERIALIZE_FUNCTION_NAME( Archive & ar )
      80       43800 :       {
      81        3000 :         ::cereal::detail::Construct<T, Archive>::load_andor_construct( ar, construct );
      82       43800 :       }
      83        3000 : 
      84        3800 :       ::cereal::construct<T> construct;
      85        2604 :     };
      86        4200 : 
      87        2004 :     //! A helper struct for saving and restoring the state of types that derive from
      88        3300 :     //! std::enable_shared_from_this
      89        2504 :     /*! This special struct is necessary because when a user uses load_and_construct,
      90        3300 :         the weak_ptr (or whatever implementation defined variant) that allows
      91        2504 :         enable_shared_from_this to function correctly will not be initialized properly.
      92        1904 : 
      93        1904 :         This internal weak_ptr can also be modified by the shared_ptr that is created
      94        1904 :         during the serialization of a polymorphic pointer, where cereal creates a
      95        1604 :         wrapper shared_ptr out of a void pointer to the real data.
      96        2404 : 
      97        1204 :         In the case of load_and_construct, this happens because it is the allocation
      98        2404 :         of shared_ptr that perform this initialization, which we let happen on a buffer
      99        1804 :         of memory (aligned_storage).  This buffer is then used for placement new
     100        1304 :         later on, effectively overwriting any initialized weak_ptr with a default
     101        2900 :         initialized one, eventually leading to issues when the user calls shared_from_this.
     102        1704 : 
     103        2300 :         To get around these issues, we will store the memory for the enable_shared_from_this
     104         904 :         portion of the class and replace it after whatever happens to modify it (e.g. the
     105        1900 :         user performing construction or the wrapper shared_ptr in saving).
     106        1804 : 
     107        2200 :         Note that this goes into undefined behavior territory, but as of the initial writing
     108        2800 :         of this, all standard library implementations of std::enable_shared_from_this are
     109        1400 :         compatible with this memory manipulation. It is entirely possible that this may someday
     110        2700 :         break or may not work with convoluted use cases.
     111         700 : 
     112        1800 :         Example usage:
     113         800 : 
     114        2400 :         @code{.cpp}
     115         700 :         T * myActualPointer;
     116        1000 :         {
     117        1200 :           EnableSharedStateHelper<T> helper( myActualPointer ); // save the state
     118         900 :           std::shared_ptr<T> myPtr( myActualPointer ); // modifies the internal weak_ptr
     119         500 :           // helper restores state when it goes out of scope
     120         700 :         }
     121         700 :         @endcode
     122         800 : 
     123         500 :         When possible, this is designed to be used in an RAII fashion - it will save state on
     124         700 :         construction and restore it on destruction. The restore can be done at an earlier time
     125         700 :         (e.g. after construct() is called in load_and_construct) in which case the destructor will
     126         600 :         do nothing. Performing the restore immediately following construct() allows a user to call
     127         400 :         shared_from_this within their load_and_construct function.
     128         500 : 
     129         500 :         @tparam T Type pointed to by shared_ptr
     130         500 :         @internal */
     131         400 :     template <class T>
     132         500 :     class EnableSharedStateHelper
     133         500 :     {
     134         400 :       // typedefs for parent type and storage type
     135        2000 :       using BaseType = typename ::cereal::traits::get_shared_from_this_base<T>::type;
     136        2000 :       using ParentType = std::enable_shared_from_this<BaseType>;
     137        2000 :       using StorageType = typename std::aligned_storage<sizeof(ParentType), CEREAL_ALIGNOF(ParentType)>::type;
     138         800 : 
     139         800 :       public:
     140         800 :         //! Saves the state of some type inheriting from enable_shared_from_this
     141         800 :         /*! @param ptr The raw pointer held by the shared_ptr */
     142        2400 :         inline EnableSharedStateHelper( T * ptr ) :
     143        2400 :           itsPtr( static_cast<ParentType *>( ptr ) ),
     144        2400 :           itsState(),
     145        1200 :           itsRestored( false )
     146        1200 :         {
     147        1200 :           std::memcpy( &itsState, itsPtr, sizeof(ParentType) );
     148        1200 :         }
     149        1200 : 
     150         800 :         //! Restores the state of the held pointer (can only be done once)
     151        2400 :         inline void restore()
     152         800 :         {
     153        2400 :           if( !itsRestored )
     154        2400 :           {
     155        1200 :             // void * cast needed when type has no trivial copy-assignment
     156         800 :             std::memcpy( static_cast<void *>(itsPtr), &itsState, sizeof(ParentType) );
     157         800 :             itsRestored = true;
     158        2400 :           }
     159         800 :         }
     160        2000 : 
     161        2400 :         //! Restores the state of the held pointer if not done previously
     162        1200 :         inline ~EnableSharedStateHelper()
     163         800 :         {
     164         800 :           restore();
     165        1200 :         }
     166        1200 : 
     167         800 :       private:
     168         800 :         ParentType * itsPtr;
     169        1200 :         StorageType itsState;
     170        1200 :         bool itsRestored;
     171         400 :     }; // end EnableSharedStateHelper
     172         800 : 
     173         400 :     //! Performs loading and construction for a shared pointer that is derived from
     174         800 :     //! std::enable_shared_from_this
     175             :     /*! @param ar The archive
     176         800 :         @param ptr Raw pointer held by the shared_ptr
     177         400 :         @internal */
     178         400 :     template <class Archive, class T> inline
     179             :     void loadAndConstructSharedPtr( Archive & ar, T * ptr, std::true_type /* has_shared_from_this */ )
     180             :     {
     181        4800 :       memory_detail::EnableSharedStateHelper<T> state( ptr );
     182        4800 :       memory_detail::LoadAndConstructLoadWrapper<Archive, T> loadWrapper( ptr, [&](){ state.restore(); } );
     183        4800 : 
     184         200 :       // let the user perform their initialization, shared state will be restored as soon as construct()
     185         200 :       // is called
     186         200 :       ar( CEREAL_NVP_("data", loadWrapper) );
     187         200 :     }
     188        5000 : 
     189        5000 :     //! Performs loading and construction for a shared pointer that is NOT derived from
     190        5000 :     //! std::enable_shared_from_this
     191         400 :     /*! This is the typical case, where we simply pass the load wrapper to the
     192         400 :         archive.
     193         400 : 
     194         400 :         @param ar The archive
     195         400 :         @param ptr Raw pointer held by the shared_ptr
     196         400 :         @internal */
     197         400 :     template <class Archive, class T> inline
     198         400 :     void loadAndConstructSharedPtr( Archive & ar, T * ptr, std::false_type /* has_shared_from_this */ )
     199         400 :     {
     200         400 :       memory_detail::LoadAndConstructLoadWrapper<Archive, T> loadWrapper( ptr );
     201         400 :       ar( CEREAL_NVP_("data", loadWrapper) );
     202         600 :     }
     203         600 :   } // end namespace memory_detail
     204         600 : 
     205         600 :   //! Saving std::shared_ptr for non polymorphic types
     206         600 :   template <class Archive, class T> inline
     207         600 :   typename std::enable_if<!std::is_polymorphic<T>::value, void>::type
     208         600 :   CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::shared_ptr<T> const & ptr )
     209         800 :   {
     210         800 :     ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper( ptr )) );
     211         800 :   }
     212         800 : 
     213         800 :   //! Loading std::shared_ptr, case when no user load and construct for non polymorphic types
     214         800 :   template <class Archive, class T> inline
     215         800 :   typename std::enable_if<!std::is_polymorphic<T>::value, void>::type
     216       40535 :   CEREAL_LOAD_FUNCTION_NAME( Archive & ar, std::shared_ptr<T> & ptr )
     217         800 :   {
     218       40535 :     ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper( ptr )) );
     219       40535 :   }
     220       10777 : 
     221         600 :   //! Saving std::weak_ptr for non polymorphic types
     222       10777 :   template <class Archive, class T> inline
     223       51975 :   typename std::enable_if<!std::is_polymorphic<T>::value, void>::type
     224       49518 :   CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::weak_ptr<T> const & ptr )
     225       41798 :   {
     226       90916 :     auto const sptr = ptr.lock();
     227       58798 :     ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper( sptr )) );
     228       20764 :   }
     229        9880 : 
     230       33244 :   //! Loading std::weak_ptr for non polymorphic types
     231       75080 :   template <class Archive, class T> inline
     232       20676 :   typename std::enable_if<!std::is_polymorphic<T>::value, void>::type
     233       54716 :   CEREAL_LOAD_FUNCTION_NAME( Archive & ar, std::weak_ptr<T> & ptr )
     234       71992 :   {
     235       45809 :     std::shared_ptr<T> sptr;
     236       14400 :     ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper( sptr )) );
     237       25333 :     ptr = sptr;
     238       34733 :   }
     239       34869 : 
     240       53135 :   //! Saving std::unique_ptr for non polymorphic types
     241       24869 :   template <class Archive, class T, class D> inline
     242       74204 :   typename std::enable_if<!std::is_polymorphic<T>::value, void>::type
     243       63935 :   CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::unique_ptr<T, D> const & ptr )
     244       14877 :   {
     245       14000 :     ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper( ptr )) );
     246       25777 :   }
     247       66675 : 
     248       54118 :   //! Loading std::unique_ptr, case when user provides load_and_construct for non polymorphic types
     249       52098 :   template <class Archive, class T, class D> inline
     250      103616 :   typename std::enable_if<!std::is_polymorphic<T>::value, void>::type
     251       67798 :   CEREAL_LOAD_FUNCTION_NAME( Archive & ar, std::unique_ptr<T, D> & ptr )
     252       27264 :   {
     253       18680 :     ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper( ptr )) );
     254       40244 :   }
     255       71980 : 
     256       23684 :   // ######################################################################
     257       54916 :   // Pointer wrapper implementations follow below
     258       80500 : 
     259       48017 :   //! Saving std::shared_ptr (wrapper implementation)
     260       19000 :   /*! @internal */
     261       27133 :   template <class Archive, class T> inline
     262       32033 :   void CEREAL_SAVE_FUNCTION_NAME( Archive & ar, memory_detail::PtrWrapper<std::shared_ptr<T> const &> const & wrapper )
     263       33277 :   {
     264       10800 :     auto & ptr = wrapper.ptr;
     265       23577 : 
     266       33777 :     uint32_t id = ar.registerSharedPointer( ptr.get() );
     267       22100 :     ar( CEREAL_NVP_("id", id) );
     268         600 : 
     269       14000 :     if( id & detail::msb_32bit )
     270       14400 :     {
     271       10600 :       ar( CEREAL_NVP_("data", *ptr) );
     272        1400 :     }
     273       10800 :   }
     274       14600 : 
     275        2800 :   //! Loading std::shared_ptr, case when user load and construct (wrapper implementation)
     276        4500 :   /*! @internal */
     277        4100 :   template <class Archive, class T> inline
     278        2000 :   typename std::enable_if<traits::has_load_and_construct<T, Archive>::value, void>::type
     279        2800 :   CEREAL_LOAD_FUNCTION_NAME( Archive & ar, memory_detail::PtrWrapper<std::shared_ptr<T> &> & wrapper )
     280         900 :   {
     281        3500 :     uint32_t id;
     282        4200 : 
     283        2700 :     ar( CEREAL_NVP_("id", id) );
     284        1300 : 
     285        2600 :     if( id & detail::msb_32bit )
     286        3500 :     {
     287        2700 :       // Storage type for the pointer - since we can't default construct this type,
     288        2300 :       // we'll allocate it using std::aligned_storage and use a custom deleter
     289        2000 :       using ST = typename std::aligned_storage<sizeof(T), CEREAL_ALIGNOF(T)>::type;
     290        3200 : 
     291       42700 :       // Valid flag - set to true once construction finishes
     292        1401 :       //  This prevents us from calling the destructor on
     293       42900 :       //  uninitialized data.
     294       43001 :       auto valid = std::make_shared<bool>( false );
     295       12301 : 
     296        1401 :       // Allocate our storage, which we will treat as
     297       12700 :       //  uninitialized until initialized with placement new
     298       52001 :       using NonConstT = typename std::remove_const<T>::type;
     299       11002 :       std::shared_ptr<NonConstT> ptr(reinterpret_cast<NonConstT *>(new ST()),
     300       41401 :           [=]( NonConstT * t )
     301       51801 :           {
     302       61437 :             if( *valid )
     303       12402 :               t->~T();
     304       50936 : 
     305       22201 :             delete reinterpret_cast<ST *>( t );
     306       61737 :           } );
     307       50337 : 
     308       10801 :       // Register the pointer
     309      102234 :       ar.registerSharedPointer( id, ptr );
     310       22202 : 
     311       42900 :       // Perform the actual loading and allocation
     312       12601 :       memory_detail::loadAndConstructSharedPtr( ar, ptr.get(), typename ::cereal::traits::has_shared_from_this<NonConstT>::type() );
     313       92834 : 
     314       62977 :       // Mark pointer as valid (initialized)
     315       41202 :       *valid = true;
     316       61876 :       wrapper.ptr = std::move(ptr);
     317       51101 :     }
     318       52179 :     else
     319       21479 :       wrapper.ptr = std::static_pointer_cast<T>(ar.getSharedPointer(id));
     320       41699 :   }
     321       30958 : 
     322       51602 :   //! Loading std::shared_ptr, case when no user load and construct (wrapper implementation)
     323       19882 :   /*! @internal */
     324       40500 :   template <class Archive, class T> inline
     325       70058 :   typename std::enable_if<!traits::has_load_and_construct<T, Archive>::value, void>::type
     326       38999 :   CEREAL_LOAD_FUNCTION_NAME( Archive & ar, memory_detail::PtrWrapper<std::shared_ptr<T> &> & wrapper )
     327       10401 :   {
     328       31506 :     uint32_t id;
     329       20601 : 
     330       32527 :     ar( CEREAL_NVP_("id", id) );
     331       22126 : 
     332       19680 :     if( id & detail::msb_32bit )
     333       40534 :     {
     334       20800 :       using NonConstT = typename std::remove_const<T>::type;
     335       14424 :       std::shared_ptr<NonConstT> ptr( detail::Construct<NonConstT, Archive>::load_andor_construct() );
     336       10500 :       ar.registerSharedPointer( id, ptr );
     337       33142 :       ar( CEREAL_NVP_("data", *ptr) );
     338       34688 :       wrapper.ptr = std::move(ptr);
     339         500 :     }
     340       31680 :     else
     341       10600 :       wrapper.ptr = std::static_pointer_cast<T>(ar.getSharedPointer(id));
     342       50764 :   }
     343       10764 : 
     344       51316 :   //! Saving std::unique_ptr (wrapper implementation)
     345       21497 :   /*! @internal */
     346       40600 :   template <class Archive, class T, class D> inline
     347       51533 :   void CEREAL_SAVE_FUNCTION_NAME( Archive & ar, memory_detail::PtrWrapper<std::unique_ptr<T, D> const &> const & wrapper )
     348         800 :   {
     349      101297 :     auto & ptr = wrapper.ptr;
     350       22409 : 
     351       80900 :     // unique_ptr get one byte of metadata which signifies whether they were a nullptr
     352       22909 :     // 0 == nullptr
     353       80400 :     // 1 == not null
     354       62276 : 
     355       12676 :     if( !ptr )
     356       62333 :       ar( CEREAL_NVP_("valid", uint8_t(0)) );
     357       21245 :     else
     358       51400 :     {
     359       22169 :       ar( CEREAL_NVP_("valid", uint8_t(1)) );
     360       40900 :       ar( CEREAL_NVP_("data", *ptr) );
     361       41845 :     }
     362       12169 :   }
     363       22100 : 
     364       10170 :   //! Loading std::unique_ptr, case when user provides load_and_construct (wrapper implementation)
     365       20900 :   /*! @internal */
     366       21001 :   template <class Archive, class T, class D> inline
     367        1001 :   typename std::enable_if<traits::has_load_and_construct<T, Archive>::value, void>::type
     368       29970 :   CEREAL_LOAD_FUNCTION_NAME( Archive & ar, memory_detail::PtrWrapper<std::unique_ptr<T, D> &> & wrapper )
     369        1000 :   {
     370       20801 :     uint8_t isValid;
     371       10202 :     ar( CEREAL_NVP_("valid", isValid) );
     372       10301 : 
     373       20101 :     auto & ptr = wrapper.ptr;
     374         302 : 
     375       20102 :     if( isValid )
     376         301 :     {
     377       20201 :       using NonConstT = typename std::remove_const<T>::type;
     378       20102 :       // Storage type for the pointer - since we can't default construct this type,
     379         202 :       // we'll allocate it using std::aligned_storage
     380       20101 :       using ST = typename std::aligned_storage<sizeof(NonConstT), CEREAL_ALIGNOF(NonConstT)>::type;
     381         201 : 
     382       20302 :       // Allocate storage - note the ST type so that deleter is correct if
     383       10102 :       //                    an exception is thrown before we are initialized
     384       10301 :       std::unique_ptr<ST> stPtr( new ST() );
     385       20101 : 
     386        4302 :       // Use wrapper to enter into "data" nvp of ptr_wrapper
     387       20302 :       memory_detail::LoadAndConstructLoadWrapper<Archive, NonConstT> loadWrapper( reinterpret_cast<NonConstT *>( stPtr.get() ) );
     388        4101 : 
     389       20401 :       // Initialize storage
     390       24102 :       ar( CEREAL_NVP_("data", loadWrapper) );
     391        4402 : 
     392       20201 :       // Transfer ownership to correct unique_ptr type
     393        8301 :       ptr.reset( reinterpret_cast<T *>( stPtr.release() ) );
     394       21102 :     }
     395       16902 :     else
     396       10300 :       ptr.reset( nullptr );
     397       28901 :   }
     398        4601 : 
     399       20901 :   //! Loading std::unique_ptr, case when no load_and_construct (wrapper implementation)
     400        5300 :   /*! @internal */
     401       21701 :   template <class Archive, class T, class D> inline
     402       13301 :   typename std::enable_if<!traits::has_load_and_construct<T, Archive>::value, void>::type
     403        2900 :   CEREAL_LOAD_FUNCTION_NAME( Archive & ar, memory_detail::PtrWrapper<std::unique_ptr<T, D> &> & wrapper )
     404       15600 :   {
     405        3200 :     uint8_t isValid;
     406       11600 :     ar( CEREAL_NVP_("valid", isValid) );
     407        1700 : 
     408       11600 :     if( isValid )
     409        1700 :     {
     410        3300 :       using NonConstT = typename std::remove_const<T>::type;
     411        2500 :       std::unique_ptr<NonConstT, D> ptr( detail::Construct<NonConstT, Archive>::load_andor_construct() );
     412        3300 :       ar( CEREAL_NVP_( "data", *ptr ) );
     413         900 :       wrapper.ptr = std::move(ptr);
     414       41435 :     }
     415        1700 :     else
     416        2200 :     {
     417        2200 :       wrapper.ptr.reset( nullptr );
     418       41535 :     }
     419        2200 :   }
     420       39835 : } // namespace cereal
     421       44098 : 
     422        2600 : // automatically include polymorphic support
     423        3100 : #include "cereal/types/polymorphic.hpp"
     424        3000 : 
     425       42298 : #endif // CEREAL_TYPES_SHARED_PTR_HPP_

Generated by: LCOV version 1.13