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