LCOV - code coverage report
Current view: top level - cereal/types - memory.hpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 109 112 97.3 %
Date: 2017-02-12 13:57:59 Functions: 598 638 93.7 %

          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             : // 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             :   {
      48             :     //! A wrapper class to notify cereal that it is ok to serialize the contained pointer
      49             :     /*! This mechanism allows us to intercept and properly handle polymorphic pointers
      50             :         @internal */
      51             :     template<class T>
      52             :     struct PtrWrapper
      53             :     {
      54      182008 :       PtrWrapper(T && p) : ptr(std::forward<T>(p)) {}
      55             :       T & ptr;
      56             : 
      57             :       PtrWrapper & operator=( PtrWrapper const & ) = delete;
      58             :     };
      59             : 
      60             :     //! Make a PtrWrapper
      61             :     /*! @internal */
      62             :     template<class T> inline
      63      182008 :     PtrWrapper<T> make_ptr_wrapper(T && t)
      64             :     {
      65      182008 :       return {std::forward<T>(t)};
      66             :     }
      67             : 
      68             :     //! A struct that acts as a wrapper around calling load_andor_construct
      69             :     /*! The purpose of this is to allow a load_and_construct call to properly enter into the
      70             :         'data' NVP of the ptr_wrapper
      71             :         @internal */
      72             :     template <class Archive, class T>
      73        4800 :     struct LoadAndConstructLoadWrapper
      74             :     {
      75        3600 :       LoadAndConstructLoadWrapper( T * ptr ) :
      76        3600 :         construct( ptr )
      77        3600 :       { }
      78             : 
      79             :       //! Constructor for embedding an early call for restoring shared_from_this
      80             :       template <class F>
      81        1200 :       LoadAndConstructLoadWrapper( T * ptr, F && sharedFromThisFunc ) :
      82        1200 :         construct( ptr, sharedFromThisFunc )
      83        1200 :       { }
      84             : 
      85        4800 :       inline void CEREAL_SERIALIZE_FUNCTION_NAME( Archive & ar )
      86             :       {
      87        4800 :         ::cereal::detail::Construct<T, Archive>::load_andor_construct( ar, construct );
      88        4400 :       }
      89             : 
      90             :       ::cereal::construct<T> construct;
      91             :     };
      92             : 
      93             :     //! A helper struct for saving and restoring the state of types that derive from
      94             :     //! std::enable_shared_from_this
      95             :     /*! This special struct is necessary because when a user uses load_and_construct,
      96             :         the weak_ptr (or whatever implementation defined variant) that allows
      97             :         enable_shared_from_this to function correctly will not be initialized properly.
      98             : 
      99             :         This internal weak_ptr can also be modified by the shared_ptr that is created
     100             :         during the serialization of a polymorphic pointer, where cereal creates a
     101             :         wrapper shared_ptr out of a void pointer to the real data.
     102             : 
     103             :         In the case of load_and_construct, this happens because it is the allocation
     104             :         of shared_ptr that perform this initialization, which we let happen on a buffer
     105             :         of memory (aligned_storage).  This buffer is then used for placement new
     106             :         later on, effectively overwriting any initialized weak_ptr with a default
     107             :         initialized one, eventually leading to issues when the user calls shared_from_this.
     108             : 
     109             :         To get around these issues, we will store the memory for the enable_shared_from_this
     110             :         portion of the class and replace it after whatever happens to modify it (e.g. the
     111             :         user performing construction or the wrapper shared_ptr in saving).
     112             : 
     113             :         Example usage:
     114             : 
     115             :         @code{.cpp}
     116             :         T * myActualPointer;
     117             :         {
     118             :           EnableSharedStateHelper<T> helper( myActualPointer ); // save the state
     119             :           std::shared_ptr<T> myPtr( myActualPointer ); // modifies the internal weak_ptr
     120             :           // helper restores state when it goes out of scope
     121             :         }
     122             :         @endcode
     123             : 
     124             :         When possible, this is designed to be used in an RAII fashion - it will save state on
     125             :         construction and restore it on destruction. The restore can be done at an earlier time
     126             :         (e.g. after construct() is called in load_and_construct) in which case the destructor will
     127             :         do nothing. Performing the restore immediately following construct() allows a user to call
     128             :         shared_from_this within their load_and_construct function.
     129             : 
     130             :         @tparam T Type pointed to by shared_ptr
     131             :         @internal */
     132             :     template <class T>
     133             :     class EnableSharedStateHelper
     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:
     141             :         //! Saves the state of some type inheriting from enable_shared_from_this
     142             :         /*! @param ptr The raw pointer held by the shared_ptr */
     143        2000 :         inline EnableSharedStateHelper( T * ptr ) :
     144             :           itsPtr( static_cast<ParentType *>( ptr ) ),
     145             :           itsState(),
     146        2000 :           itsRestored( false )
     147             :         {
     148        2000 :           std::memcpy( &itsState, itsPtr, sizeof(ParentType) );
     149        2000 :         }
     150             : 
     151             :         //! Restores the state of the held pointer (can only be done once)
     152        3200 :         inline void restore()
     153             :         {
     154        3200 :           if( !itsRestored )
     155             :           {
     156        2000 :             std::memcpy( itsPtr, &itsState, sizeof(ParentType) );
     157        2000 :             itsRestored = true;
     158             :           }
     159        3200 :         }
     160             : 
     161             :         //! Restores the state of the held pointer if not done previously
     162        2000 :         inline ~EnableSharedStateHelper()
     163             :         {
     164        2000 :           restore();
     165        2000 :         }
     166             : 
     167             :       private:
     168             :         ParentType * itsPtr;
     169             :         StorageType itsState;
     170             :         bool itsRestored;
     171             :     }; // end EnableSharedStateHelper
     172             : 
     173             :     //! Performs loading and construction for a shared pointer that is derived from
     174             :     //! std::enable_shared_from_this
     175             :     /*! @param ar The archive
     176             :         @param ptr Raw pointer held by the shared_ptr
     177             :         @internal */
     178             :     template <class Archive, class T> inline
     179        1200 :     void loadAndConstructSharedPtr( Archive & ar, T * ptr, std::true_type /* has_shared_from_this */ )
     180             :     {
     181        2400 :       memory_detail::EnableSharedStateHelper<T> state( ptr );
     182        3600 :       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        1200 :       ar( CEREAL_NVP_("data", loadWrapper) );
     187        1200 :     }
     188             : 
     189             :     //! Performs loading and construction for a shared pointer that is NOT derived from
     190             :     //! std::enable_shared_from_this
     191             :     /*! This is the typical case, where we simply pass the load wrapper to the
     192             :         archive.
     193             : 
     194             :         @param ar The archive
     195             :         @param ptr Raw pointer held by the shared_ptr
     196             :         @internal */
     197             :     template <class Archive, class T> inline
     198        2000 :     void loadAndConstructSharedPtr( Archive & ar, T * ptr, std::false_type /* has_shared_from_this */ )
     199             :     {
     200        4000 :       memory_detail::LoadAndConstructLoadWrapper<Archive, T> loadWrapper( ptr );
     201        2000 :       ar( CEREAL_NVP_("data", loadWrapper) );
     202        2000 :     }
     203             :   } // end namespace memory_detail
     204             : 
     205             :   //! Saving std::shared_ptr for non polymorphic types
     206             :   template <class Archive, class T> inline
     207             :   typename std::enable_if<!std::is_polymorphic<T>::value, void>::type
     208       45204 :   CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::shared_ptr<T> const & ptr )
     209             :   {
     210       45204 :     ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper( ptr )) );
     211       45204 :   }
     212             : 
     213             :   //! Loading std::shared_ptr, case when no user load and construct for non polymorphic types
     214             :   template <class Archive, class T> inline
     215             :   typename std::enable_if<!std::is_polymorphic<T>::value, void>::type
     216       45204 :   CEREAL_LOAD_FUNCTION_NAME( Archive & ar, std::shared_ptr<T> & ptr )
     217             :   {
     218       45204 :     ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper( ptr )) );
     219       45204 :   }
     220             : 
     221             :   //! Saving std::weak_ptr for non polymorphic types
     222             :   template <class Archive, class T> inline
     223             :   typename std::enable_if<!std::is_polymorphic<T>::value, void>::type
     224         800 :   CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::weak_ptr<T> const & ptr )
     225             :   {
     226        1600 :     auto const sptr = ptr.lock();
     227         800 :     ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper( sptr )) );
     228         800 :   }
     229             : 
     230             :   //! Loading std::weak_ptr for non polymorphic types
     231             :   template <class Archive, class T> inline
     232             :   typename std::enable_if<!std::is_polymorphic<T>::value, void>::type
     233         800 :   CEREAL_LOAD_FUNCTION_NAME( Archive & ar, std::weak_ptr<T> & ptr )
     234             :   {
     235        1600 :     std::shared_ptr<T> sptr;
     236         800 :     ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper( sptr )) );
     237         800 :     ptr = sptr;
     238         800 :   }
     239             : 
     240             :   //! Saving std::unique_ptr for non polymorphic types
     241             :   template <class Archive, class T, class D> inline
     242             :   typename std::enable_if<!std::is_polymorphic<T>::value, void>::type
     243       41200 :   CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::unique_ptr<T, D> const & ptr )
     244             :   {
     245       41200 :     ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper( ptr )) );
     246       41200 :   }
     247             : 
     248             :   //! Loading std::unique_ptr, case when user provides load_and_construct for non polymorphic types
     249             :   template <class Archive, class T, class D> inline
     250             :   typename std::enable_if<!std::is_polymorphic<T>::value, void>::type
     251       41600 :   CEREAL_LOAD_FUNCTION_NAME( Archive & ar, std::unique_ptr<T, D> & ptr )
     252             :   {
     253       42000 :     ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper( ptr )) );
     254       41200 :   }
     255             : 
     256             :   // ######################################################################
     257             :   // Pointer wrapper implementations follow below
     258             : 
     259             :   //! Saving std::shared_ptr (wrapper implementation)
     260             :   /*! @internal */
     261             :   template <class Archive, class T> inline
     262       48804 :   void CEREAL_SAVE_FUNCTION_NAME( Archive & ar, memory_detail::PtrWrapper<std::shared_ptr<T> const &> const & wrapper )
     263             :   {
     264       48804 :     auto & ptr = wrapper.ptr;
     265             : 
     266       48804 :     uint32_t id = ar.registerSharedPointer( ptr.get() );
     267       48804 :     ar( CEREAL_NVP_("id", id) );
     268             : 
     269       48804 :     if( id & detail::msb_32bit )
     270             :     {
     271       45604 :       ar( CEREAL_NVP_("data", *ptr) );
     272             :     }
     273       48804 :   }
     274             : 
     275             :   //! Loading std::shared_ptr, case when user load and construct (wrapper implementation)
     276             :   /*! @internal */
     277             :   template <class Archive, class T> inline
     278             :   typename std::enable_if<traits::has_load_and_construct<T, Archive>::value, void>::type
     279        3600 :   CEREAL_LOAD_FUNCTION_NAME( Archive & ar, memory_detail::PtrWrapper<std::shared_ptr<T> &> & wrapper )
     280             :   {
     281        3600 :     auto & ptr = wrapper.ptr;
     282             : 
     283             :     uint32_t id;
     284             : 
     285        3600 :     ar( CEREAL_NVP_("id", id) );
     286             : 
     287        3600 :     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        6400 :       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        3200 :       ptr.reset( reinterpret_cast<T *>( new ST() ),
     301       44800 :           [=]( T * t )
     302             :           {
     303        3200 :             if( *valid )
     304        1600 :               t->~T();
     305             : 
     306        3200 :             delete reinterpret_cast<ST *>( t );
     307        3200 :           } );
     308             : 
     309             :       // Register the pointer
     310        3200 :       ar.registerSharedPointer( id, ptr );
     311             : 
     312             :       // Perform the actual loading and allocation
     313        3200 :       memory_detail::loadAndConstructSharedPtr( ar, ptr.get(), typename ::cereal::traits::has_shared_from_this<T>::type() );
     314             : 
     315             :       // Mark pointer as valid (initialized)
     316        3200 :       *valid = true;
     317             :     }
     318             :     else
     319         400 :       ptr = std::static_pointer_cast<T>(ar.getSharedPointer(id));
     320        3600 :   }
     321             : 
     322             :   //! Loading std::shared_ptr, case when no user load and construct (wrapper implementation)
     323             :   /*! @internal */
     324             :   template <class Archive, class T> inline
     325             :   typename std::enable_if<!traits::has_load_and_construct<T, Archive>::value, void>::type
     326       45204 :   CEREAL_LOAD_FUNCTION_NAME( Archive & ar, memory_detail::PtrWrapper<std::shared_ptr<T> &> & wrapper )
     327             :   {
     328       45204 :     auto & ptr = wrapper.ptr;
     329             : 
     330             :     uint32_t id;
     331             : 
     332       45204 :     ar( CEREAL_NVP_("id", id) );
     333             : 
     334       45204 :     if( id & detail::msb_32bit )
     335             :     {
     336       42404 :       ptr.reset( detail::Construct<T, Archive>::load_andor_construct() );
     337       42404 :       ar.registerSharedPointer( id, ptr );
     338       42404 :       ar( CEREAL_NVP_("data", *ptr) );
     339             :     }
     340             :     else
     341        2800 :       ptr = std::static_pointer_cast<T>(ar.getSharedPointer(id));
     342       45204 :   }
     343             : 
     344             :   //! Saving std::unique_ptr (wrapper implementation)
     345             :   /*! @internal */
     346             :   template <class Archive, class T, class D> inline
     347       42000 :   void CEREAL_SAVE_FUNCTION_NAME( Archive & ar, memory_detail::PtrWrapper<std::unique_ptr<T, D> const &> const & wrapper )
     348             :   {
     349       42000 :     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       42000 :     if( !ptr )
     356           0 :       ar( CEREAL_NVP_("valid", uint8_t(0)) );
     357             :     else
     358             :     {
     359       42000 :       ar( CEREAL_NVP_("valid", uint8_t(1)) );
     360       42000 :       ar( CEREAL_NVP_("data", *ptr) );
     361             :     }
     362       42000 :   }
     363             : 
     364             :   //! Loading std::unique_ptr, case when user provides load_and_construct (wrapper implementation)
     365             :   /*! @internal */
     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        1600 :   CEREAL_LOAD_FUNCTION_NAME( Archive & ar, memory_detail::PtrWrapper<std::unique_ptr<T, D> &> & wrapper )
     369             :   {
     370             :     uint8_t isValid;
     371        1600 :     ar( CEREAL_NVP_("valid", isValid) );
     372             : 
     373        1600 :     auto & ptr = wrapper.ptr;
     374             : 
     375        1600 :     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        3200 :       std::unique_ptr<ST> stPtr( new ST() );
     384             : 
     385             :       // Use wrapper to enter into "data" nvp of ptr_wrapper
     386        3200 :       memory_detail::LoadAndConstructLoadWrapper<Archive, T> loadWrapper( reinterpret_cast<T *>( stPtr.get() ) );
     387             : 
     388             :       // Initialize storage
     389        1900 :       ar( CEREAL_NVP_("data", loadWrapper) );
     390             : 
     391             :       // Transfer ownership to correct unique_ptr type
     392        1200 :       ptr.reset( reinterpret_cast<T *>( stPtr.release() ) );
     393             :     }
     394             :     else
     395           0 :       ptr.reset( nullptr );
     396        1200 :   }
     397             : 
     398             :   //! Loading std::unique_ptr, case when no load_and_construct (wrapper implementation)
     399             :   /*! @internal */
     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       40800 :   CEREAL_LOAD_FUNCTION_NAME( Archive & ar, memory_detail::PtrWrapper<std::unique_ptr<T, D> &> & wrapper )
     403             :   {
     404             :     uint8_t isValid;
     405       40800 :     ar( CEREAL_NVP_("valid", isValid) );
     406             : 
     407       40800 :     auto & ptr = wrapper.ptr;
     408             : 
     409       40800 :     if( isValid )
     410             :     {
     411       40800 :       ptr.reset( detail::Construct<T, Archive>::load_andor_construct() );
     412       40800 :       ar( CEREAL_NVP_( "data", *ptr ) );
     413             :     }
     414             :     else
     415             :     {
     416           0 :       ptr.reset( nullptr );
     417             :     }
     418       40800 :   }
     419             : } // namespace cereal
     420             : 
     421             : // automatically include polymorphic support
     422             : #include "cereal/types/polymorphic.hpp"
     423             : 
     424             : #undef CEREAL_ALIGNOF
     425             : #endif // CEREAL_TYPES_SHARED_PTR_HPP_

Generated by: LCOV version 1.11