LCOV - code coverage report
Current view: top level - cereal/types - polymorphic.hpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 64 88 72.7 %
Date: 2022-01-16 21:05:07 Functions: 136 144 94.4 %

          Line data    Source code
       1             : /*! \file polymorphic.hpp
       2             :     \brief Support for pointers to polymorphic base classes
       3             :     \ingroup OtherTypes */
       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_POLYMORPHIC_HPP_
      31             : #define CEREAL_TYPES_POLYMORPHIC_HPP_
      32             : 
      33             : #include "cereal/cereal.hpp"
      34             : #include "cereal/types/memory.hpp"
      35             : 
      36             : #include "cereal/details/util.hpp"
      37             : #include "cereal/details/helpers.hpp"
      38             : #include "cereal/details/traits.hpp"
      39             : #include "cereal/details/polymorphic_impl.hpp"
      40             : 
      41             : #if defined(_MSC_VER) && _MSC_VER < 1916
      42             : #define CEREAL_STATIC_CONSTEXPR static
      43             : #else
      44             : #define CEREAL_STATIC_CONSTEXPR static constexpr
      45             : #endif
      46             : 
      47             : //! Registers a derived polymorphic type with cereal
      48             : /*! Polymorphic types must be registered before smart
      49             :     pointers to them can be serialized.  Note that base
      50             :     classes do not need to be registered.
      51             : 
      52             :     Registering a type lets cereal know how to properly
      53             :     serialize it when a smart pointer to a base object is
      54             :     used in conjunction with a derived class.
      55             : 
      56             :     This assumes that all relevant archives have also
      57             :     previously been registered.  Registration for archives
      58             :     is usually done in the header file in which they are
      59             :     defined.  This means that type registration needs to
      60             :     happen after specific archives to be used are included.
      61             : 
      62             :     It is recommended that type registration be done in
      63             :     the header file in which the type is declared.
      64             : 
      65             :     Registration can also be placed in a source file,
      66             :     but this may require the use of the
      67             :     CEREAL_REGISTER_DYNAMIC_INIT macro (see below).
      68             : 
      69             :     Registration may be called repeatedly for the same
      70             :     type in different translation units to add support
      71             :     for additional archives if they are not initially
      72             :     available (included and registered).
      73             : 
      74             :     When building serialization support as a DLL on
      75             :     Windows, registration must happen in the header file.
      76             :     On Linux and Mac things should still work properly
      77             :     if placed in a source file, but see the above comments
      78             :     on registering in source files.
      79             : 
      80             :     Polymorphic support in cereal requires RTTI to be
      81             :     enabled */
      82             : #define CEREAL_REGISTER_TYPE(...)                                        \
      83             :   namespace cereal {                                                     \
      84             :   namespace detail {                                                     \
      85             :   template <>                                                            \
      86             :   struct binding_name<__VA_ARGS__>                                       \
      87             :   {                                                                      \
      88             :     CEREAL_STATIC_CONSTEXPR char const * name() { return #__VA_ARGS__; } \
      89             :   };                                                                     \
      90             :   } } /* end namespaces */                                               \
      91             :   CEREAL_BIND_TO_ARCHIVES(__VA_ARGS__)
      92             : 
      93             : //! Registers a polymorphic type with cereal, giving it a
      94             : //! user defined name
      95             : /*! In some cases the default name used with
      96             :     CEREAL_REGISTER_TYPE (the name of the type) may not be
      97             :     suitable.  This macro allows any name to be associated
      98             :     with the type.  The name should be unique */
      99             : #define CEREAL_REGISTER_TYPE_WITH_NAME(T, Name)                     \
     100             :   namespace cereal {                                                \
     101             :   namespace detail {                                                \
     102             :   template <>                                                       \
     103             :   struct binding_name<T>                                            \
     104             :   { CEREAL_STATIC_CONSTEXPR char const * name() { return Name; } }; \
     105             :   } } /* end namespaces */                                          \
     106             :   CEREAL_BIND_TO_ARCHIVES(T)
     107             : 
     108             : //! Registers the base-derived relationship for a polymorphic type
     109             : /*! When polymorphic serialization occurs, cereal needs to know how to
     110             :     properly cast between derived and base types for the polymorphic
     111             :     type. Normally this happens automatically whenever cereal::base_class
     112             :     or cereal::virtual_base_class are used to serialize a base class. In
     113             :     cases where neither of these is ever called but a base class still
     114             :     exists, this explicit registration is required.
     115             : 
     116             :     The Derived class should be the most derived type that will be serialized,
     117             :     and the Base type any possible base that has not been covered under a base
     118             :     class serialization that will be used to store a Derived pointer.
     119             : 
     120             :     Placement of this is the same as for CEREAL_REGISTER_TYPE. */
     121             : #define CEREAL_REGISTER_POLYMORPHIC_RELATION(Base, Derived)                     \
     122             :   namespace cereal {                                                            \
     123             :   namespace detail {                                                            \
     124             :   template <>                                                                   \
     125             :   struct PolymorphicRelation<Base, Derived>                                     \
     126             :   { static void bind() { RegisterPolymorphicCaster<Base, Derived>::bind(); } }; \
     127             :   } } /* end namespaces */
     128             : 
     129             : //! Adds a way to force initialization of a translation unit containing
     130             : //! calls to CEREAL_REGISTER_TYPE
     131             : /*! In C++, dynamic initialization of non-local variables of a translation
     132             :     unit may be deferred until "the first odr-use of any function or variable
     133             :     defined in the same translation unit as the variable to be initialized."
     134             : 
     135             :     Informally, odr-use means that your program takes the address of or binds
     136             :     a reference directly to an object, which must have a definition.
     137             : 
     138             :     Since polymorphic type support in cereal relies on the dynamic
     139             :     initialization of certain global objects happening before
     140             :     serialization is performed, it is important to ensure that something
     141             :     from files that call CEREAL_REGISTER_TYPE is odr-used before serialization
     142             :     occurs, otherwise the registration will never take place.  This may often
     143             :     be the case when serialization is built as a shared library external from
     144             :     your main program.
     145             : 
     146             :     This macro, with any name of your choosing, should be placed into the
     147             :     source file that contains calls to CEREAL_REGISTER_TYPE.
     148             : 
     149             :     Its counterpart, CEREAL_FORCE_DYNAMIC_INIT, should be placed in its
     150             :     associated header file such that it is included in the translation units
     151             :     (source files) in which you want the registration to appear.
     152             : 
     153             :     @relates CEREAL_FORCE_DYNAMIC_INIT
     154             :     */
     155             : #define CEREAL_REGISTER_DYNAMIC_INIT(LibName)                \
     156             :   namespace cereal {                                         \
     157             :   namespace detail {                                         \
     158             :     void CEREAL_DLL_EXPORT dynamic_init_dummy_##LibName() {} \
     159             :   } } /* end namespaces */
     160             : 
     161             : //! Forces dynamic initialization of polymorphic support in a
     162             : //! previously registered source file
     163             : /*! @sa CEREAL_REGISTER_DYNAMIC_INIT
     164             : 
     165             :     See CEREAL_REGISTER_DYNAMIC_INIT for detailed explanation
     166             :     of how this macro should be used.  The name used should
     167             :     match that for CEREAL_REGISTER_DYNAMIC_INIT. */
     168             : #define CEREAL_FORCE_DYNAMIC_INIT(LibName)                 \
     169             :   namespace cereal {                                       \
     170             :   namespace detail {                                       \
     171             :     void CEREAL_DLL_EXPORT dynamic_init_dummy_##LibName(); \
     172             :   } /* end detail */                                       \
     173             :   } /* end cereal */                                       \
     174             :   namespace {                                              \
     175             :     struct dynamic_init_##LibName {                        \
     176             :       dynamic_init_##LibName() {                           \
     177             :         ::cereal::detail::dynamic_init_dummy_##LibName();  \
     178             :       }                                                    \
     179             :     } dynamic_init_instance_##LibName;                     \
     180             :   } /* end anonymous namespace */
     181             : 
     182             : namespace cereal
     183             : {
     184             :   namespace polymorphic_detail
     185             :   {
     186             :     //! Error message used for unregistered polymorphic types
     187             :     /*! @internal */
     188             :     #define UNREGISTERED_POLYMORPHIC_EXCEPTION(LoadSave, Name)                                                                                      \
     189             :       throw cereal::Exception("Trying to " #LoadSave " an unregistered polymorphic type (" + Name + ").\n"                                          \
     190             :                               "Make sure your type is registered with CEREAL_REGISTER_TYPE and that the archive "                                   \
     191             :                               "you are using was included (and registered with CEREAL_REGISTER_ARCHIVE) prior to calling CEREAL_REGISTER_TYPE.\n"   \
     192             :                               "If your type is already registered and you still see this error, you may need to use CEREAL_REGISTER_DYNAMIC_INIT.");
     193             : 
     194             :     //! Get an input binding from the given archive by deserializing the type meta data
     195             :     /*! @internal */
     196             :     template<class Archive> inline
     197        5600 :     typename ::cereal::detail::InputBindingMap<Archive>::Serializers getInputBinding(Archive & ar, std::uint32_t const nameid)
     198             :     {
     199             :       // If the nameid is zero, we serialized a null pointer
     200        5600 :       if(nameid == 0)
     201             :       {
     202           0 :         typename ::cereal::detail::InputBindingMap<Archive>::Serializers emptySerializers;
     203           0 :         emptySerializers.shared_ptr = [](void*, std::shared_ptr<void> & ptr, std::type_info const &) { ptr.reset(); };
     204           0 :         emptySerializers.unique_ptr = [](void*, std::unique_ptr<void, ::cereal::detail::EmptyDeleter<void>> & ptr, std::type_info const &) { ptr.reset( nullptr ); };
     205           0 :         return emptySerializers;
     206             :       }
     207             : 
     208       11200 :       std::string name;
     209        5600 :       if(nameid & detail::msb_32bit)
     210             :       {
     211        1600 :         ar( CEREAL_NVP_("polymorphic_name", name) );
     212        1600 :         ar.registerPolymorphicName(nameid, name);
     213             :       }
     214             :       else
     215        4000 :         name = ar.getPolymorphicName(nameid);
     216             : 
     217        5600 :       auto const & bindingMap = detail::StaticObject<detail::InputBindingMap<Archive>>::getInstance().map;
     218             : 
     219        5600 :       auto binding = bindingMap.find(name);
     220        5600 :       if(binding == bindingMap.end())
     221           0 :         UNREGISTERED_POLYMORPHIC_EXCEPTION(load, name)
     222        5600 :       return binding->second;
     223             :     }
     224             : 
     225             :     //! Serialize a shared_ptr if the 2nd msb in the nameid is set, and if we can actually construct the pointee
     226             :     /*! This check lets us try and skip doing polymorphic machinery if we can get away with
     227             :         using the derived class serialize function
     228             : 
     229             :         Note that on MSVC 2013 preview, is_default_constructible<T> returns true for abstract classes with
     230             :         default constructors, but on clang/gcc this will return false.  So we also need to check for that here.
     231             :         @internal */
     232             :     template<class Archive, class T> inline
     233             :     typename std::enable_if<(traits::is_default_constructible<T>::value
     234             :                              || traits::has_load_and_construct<T, Archive>::value)
     235             :                              && !std::is_abstract<T>::value, bool>::type
     236         400 :     serialize_wrapper(Archive & ar, std::shared_ptr<T> & ptr, std::uint32_t const nameid)
     237             :     {
     238         400 :       if(nameid & detail::msb2_32bit)
     239             :       {
     240           0 :         ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper(ptr)) );
     241           0 :         return true;
     242             :       }
     243         400 :       return false;
     244             :     }
     245             : 
     246             :     //! Serialize a unique_ptr if the 2nd msb in the nameid is set, and if we can actually construct the pointee
     247             :     /*! This check lets us try and skip doing polymorphic machinery if we can get away with
     248             :         using the derived class serialize function
     249             :         @internal */
     250             :     template<class Archive, class T, class D> inline
     251             :     typename std::enable_if<(traits::is_default_constructible<T>::value
     252             :                              || traits::has_load_and_construct<T, Archive>::value)
     253             :                              && !std::is_abstract<T>::value, bool>::type
     254             :     serialize_wrapper(Archive & ar, std::unique_ptr<T, D> & ptr, std::uint32_t const nameid)
     255             :     {
     256             :       if(nameid & detail::msb2_32bit)
     257             :       {
     258             :         ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper(ptr)) );
     259             :         return true;
     260             :       }
     261             :       return false;
     262             :     }
     263             : 
     264             :     //! Serialize a shared_ptr if the 2nd msb in the nameid is set, and if we can actually construct the pointee
     265             :     /*! This case is for when we can't actually construct the shared pointer.  Normally this would be caught
     266             :         as the pointer itself is serialized, but since this is a polymorphic pointer, if we tried to serialize
     267             :         the pointer we'd end up back here recursively.  So we have to catch the error here as well, if
     268             :         this was a polymorphic type serialized by its proper pointer type
     269             :         @internal */
     270             :     template<class Archive, class T> inline
     271             :     typename std::enable_if<(!traits::is_default_constructible<T>::value
     272             :                              && !traits::has_load_and_construct<T, Archive>::value)
     273             :                              || std::is_abstract<T>::value, bool>::type
     274        4000 :     serialize_wrapper(Archive &, std::shared_ptr<T> &, std::uint32_t const nameid)
     275             :     {
     276        4000 :       if(nameid & detail::msb2_32bit)
     277           0 :         throw cereal::Exception("Cannot load a polymorphic type that is not default constructable and does not have a load_and_construct function");
     278        4000 :       return false;
     279             :     }
     280             : 
     281             :     //! Serialize a unique_ptr if the 2nd msb in the nameid is set, and if we can actually construct the pointee
     282             :     /*! This case is for when we can't actually construct the unique pointer.  Normally this would be caught
     283             :         as the pointer itself is serialized, but since this is a polymorphic pointer, if we tried to serialize
     284             :         the pointer we'd end up back here recursively.  So we have to catch the error here as well, if
     285             :         this was a polymorphic type serialized by its proper pointer type
     286             :         @internal */
     287             :     template<class Archive, class T, class D> inline
     288             :      typename std::enable_if<(!traits::is_default_constructible<T>::value
     289             :                                && !traits::has_load_and_construct<T, Archive>::value)
     290             :                                || std::is_abstract<T>::value, bool>::type
     291        1200 :     serialize_wrapper(Archive &, std::unique_ptr<T, D> &, std::uint32_t const nameid)
     292             :     {
     293        1200 :       if(nameid & detail::msb2_32bit)
     294           0 :         throw cereal::Exception("Cannot load a polymorphic type that is not default constructable and does not have a load_and_construct function");
     295        1200 :       return false;
     296             :     }
     297             :   } // polymorphic_detail
     298             : 
     299             :   // ######################################################################
     300             :   // Pointer serialization for polymorphic types
     301             : 
     302             :   //! Saving std::shared_ptr for polymorphic types, abstract
     303             :   template <class Archive, class T> inline
     304             :   typename std::enable_if<std::is_polymorphic<T>::value && std::is_abstract<T>::value, void>::type
     305        4000 :   CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::shared_ptr<T> const & ptr )
     306             :   {
     307        4000 :     if(!ptr)
     308             :     {
     309             :       // same behavior as nullptr in memory implementation
     310           0 :       ar( CEREAL_NVP_("polymorphic_id", std::uint32_t(0)) );
     311           0 :       return;
     312             :     }
     313             : 
     314        4000 :     std::type_info const & ptrinfo = typeid(*ptr.get());
     315             :     static std::type_info const & tinfo = typeid(T);
     316             :     // ptrinfo can never be equal to T info since we can't have an instance
     317             :     // of an abstract object
     318             :     //  this implies we need to do the lookup
     319             : 
     320        4000 :     auto const & bindingMap = detail::StaticObject<detail::OutputBindingMap<Archive>>::getInstance().map;
     321             : 
     322        4000 :     auto binding = bindingMap.find(std::type_index(ptrinfo));
     323        4000 :     if(binding == bindingMap.end())
     324           0 :       UNREGISTERED_POLYMORPHIC_EXCEPTION(save, cereal::util::demangle(ptrinfo.name()))
     325             : 
     326        4000 :     binding->second.shared_ptr(&ar, ptr.get(), tinfo);
     327             :   }
     328             : 
     329             :   //! Saving std::shared_ptr for polymorphic types, not abstract
     330             :   template <class Archive, class T> inline
     331             :   typename std::enable_if<std::is_polymorphic<T>::value && !std::is_abstract<T>::value, void>::type
     332         400 :   CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::shared_ptr<T> const & ptr )
     333             :   {
     334         400 :     if(!ptr)
     335             :     {
     336             :       // same behavior as nullptr in memory implementation
     337           0 :       ar( CEREAL_NVP_("polymorphic_id", std::uint32_t(0)) );
     338           0 :       return;
     339             :     }
     340             : 
     341         400 :     std::type_info const & ptrinfo = typeid(*ptr.get());
     342             :     static std::type_info const & tinfo = typeid(T);
     343             : 
     344         400 :     if(ptrinfo == tinfo)
     345             :     {
     346             :       // The 2nd msb signals that the following pointer does not need to be
     347             :       // cast with our polymorphic machinery
     348           0 :       ar( CEREAL_NVP_("polymorphic_id", detail::msb2_32bit) );
     349             : 
     350           0 :       ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper(ptr)) );
     351             : 
     352           0 :       return;
     353             :     }
     354             : 
     355         400 :     auto const & bindingMap = detail::StaticObject<detail::OutputBindingMap<Archive>>::getInstance().map;
     356             : 
     357         400 :     auto binding = bindingMap.find(std::type_index(ptrinfo));
     358         400 :     if(binding == bindingMap.end())
     359           0 :       UNREGISTERED_POLYMORPHIC_EXCEPTION(save, cereal::util::demangle(ptrinfo.name()))
     360             : 
     361         400 :     binding->second.shared_ptr(&ar, ptr.get(), tinfo);
     362             :   }
     363             : 
     364             :   //! Loading std::shared_ptr for polymorphic types
     365             :   template <class Archive, class T> inline
     366             :   typename std::enable_if<std::is_polymorphic<T>::value, void>::type
     367        4400 :   CEREAL_LOAD_FUNCTION_NAME( Archive & ar, std::shared_ptr<T> & ptr )
     368             :   {
     369             :     std::uint32_t nameid;
     370        4400 :     ar( CEREAL_NVP_("polymorphic_id", nameid) );
     371             : 
     372             :     // Check to see if we can skip all of this polymorphism business
     373        4400 :     if(polymorphic_detail::serialize_wrapper(ar, ptr, nameid))
     374           0 :       return;
     375             : 
     376        8800 :     auto binding = polymorphic_detail::getInputBinding(ar, nameid);
     377           0 :     std::shared_ptr<void> result;
     378        4400 :     binding.shared_ptr(&ar, result, typeid(T));
     379        4400 :     ptr = std::static_pointer_cast<T>(result);
     380             :   }
     381             : 
     382             :   //! Saving std::weak_ptr for polymorphic types
     383             :   template <class Archive, class T> inline
     384             :   typename std::enable_if<std::is_polymorphic<T>::value, void>::type
     385        1200 :   CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::weak_ptr<T> const & ptr )
     386             :   {
     387        1500 :     auto const sptr = ptr.lock();
     388        1200 :     ar( CEREAL_NVP_("locked_ptr", sptr) );
     389        1200 :   }
     390             : 
     391             :   //! Loading std::weak_ptr for polymorphic types
     392             :   template <class Archive, class T> inline
     393             :   typename std::enable_if<std::is_polymorphic<T>::value, void>::type
     394        1200 :   CEREAL_LOAD_FUNCTION_NAME( Archive & ar, std::weak_ptr<T> & ptr )
     395             :   {
     396        1200 :     std::shared_ptr<T> sptr;
     397        1200 :     ar( CEREAL_NVP_("locked_ptr", sptr) );
     398        1200 :     ptr = sptr;
     399        1200 :   }
     400             : 
     401             :   //! Saving std::unique_ptr for polymorphic types that are abstract
     402             :   template <class Archive, class T, class D> inline
     403             :   typename std::enable_if<std::is_polymorphic<T>::value && std::is_abstract<T>::value, void>::type
     404        1200 :   CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::unique_ptr<T, D> const & ptr )
     405             :   {
     406        1200 :     if(!ptr)
     407             :     {
     408             :       // same behavior as nullptr in memory implementation
     409           0 :       ar( CEREAL_NVP_("polymorphic_id", std::uint32_t(0)) );
     410           0 :       return;
     411             :     }
     412             : 
     413        1200 :     std::type_info const & ptrinfo = typeid(*ptr.get());
     414             :     static std::type_info const & tinfo = typeid(T);
     415             :     // ptrinfo can never be equal to T info since we can't have an instance
     416             :     // of an abstract object
     417             :     //  this implies we need to do the lookup
     418             : 
     419        1200 :     auto const & bindingMap = detail::StaticObject<detail::OutputBindingMap<Archive>>::getInstance().map;
     420             : 
     421        1200 :     auto binding = bindingMap.find(std::type_index(ptrinfo));
     422        1200 :     if(binding == bindingMap.end())
     423           0 :       UNREGISTERED_POLYMORPHIC_EXCEPTION(save, cereal::util::demangle(ptrinfo.name()))
     424             : 
     425        1200 :     binding->second.unique_ptr(&ar, ptr.get(), tinfo);
     426             :   }
     427             : 
     428             :   //! Saving std::unique_ptr for polymorphic types, not abstract
     429             :   template <class Archive, class T, class D> inline
     430             :   typename std::enable_if<std::is_polymorphic<T>::value && !std::is_abstract<T>::value, void>::type
     431             :   CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::unique_ptr<T, D> const & ptr )
     432             :   {
     433             :     if(!ptr)
     434             :     {
     435             :       // same behavior as nullptr in memory implementation
     436             :       ar( CEREAL_NVP_("polymorphic_id", std::uint32_t(0)) );
     437             :       return;
     438             :     }
     439             : 
     440             :     std::type_info const & ptrinfo = typeid(*ptr.get());
     441             :     static std::type_info const & tinfo = typeid(T);
     442             : 
     443             :     if(ptrinfo == tinfo)
     444             :     {
     445             :       // The 2nd msb signals that the following pointer does not need to be
     446             :       // cast with our polymorphic machinery
     447             :       ar( CEREAL_NVP_("polymorphic_id", detail::msb2_32bit) );
     448             : 
     449             :       ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper(ptr)) );
     450             : 
     451             :       return;
     452             :     }
     453             : 
     454             :     auto const & bindingMap = detail::StaticObject<detail::OutputBindingMap<Archive>>::getInstance().map;
     455             : 
     456             :     auto binding = bindingMap.find(std::type_index(ptrinfo));
     457             :     if(binding == bindingMap.end())
     458             :       UNREGISTERED_POLYMORPHIC_EXCEPTION(save, cereal::util::demangle(ptrinfo.name()))
     459             : 
     460             :     binding->second.unique_ptr(&ar, ptr.get(), tinfo);
     461             :   }
     462             : 
     463             :   //! Loading std::unique_ptr, case when user provides load_and_construct for polymorphic types
     464             :   template <class Archive, class T, class D> inline
     465             :   typename std::enable_if<std::is_polymorphic<T>::value, void>::type
     466        1200 :   CEREAL_LOAD_FUNCTION_NAME( Archive & ar, std::unique_ptr<T, D> & ptr )
     467             :   {
     468             :     std::uint32_t nameid;
     469        1200 :     ar( CEREAL_NVP_("polymorphic_id", nameid) );
     470             : 
     471             :     // Check to see if we can skip all of this polymorphism business
     472        1200 :     if(polymorphic_detail::serialize_wrapper(ar, ptr, nameid))
     473           0 :       return;
     474             : 
     475        2400 :     auto binding = polymorphic_detail::getInputBinding(ar, nameid);
     476        1200 :     std::unique_ptr<void, ::cereal::detail::EmptyDeleter<void>> result;
     477        1200 :     binding.unique_ptr(&ar, result, typeid(T));
     478        1200 :     ptr.reset(static_cast<T*>(result.release()));
     479             :   }
     480             : 
     481             :   #undef UNREGISTERED_POLYMORPHIC_EXCEPTION
     482             : } // namespace cereal
     483             : #endif // CEREAL_TYPES_POLYMORPHIC_HPP_

Generated by: LCOV version 1.14