LCOV - code coverage report
Current view: top level - cereal/details - polymorphic_impl.hpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 182 203 89.7 %
Date: 2017-02-12 13:57:59 Functions: 275 532 51.7 %

          Line data    Source code
       1             : /*! \file polymorphic_impl.hpp
       2             :     \brief Internal polymorphism support
       3             :     \ingroup Internal */
       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             : 
      31             : /* This code is heavily inspired by the boost serialization implementation by the following authors
      32             : 
      33             :    (C) Copyright 2002 Robert Ramey - http://www.rrsd.com .
      34             :    Use, modification and distribution is subject to the Boost Software
      35             :    License, Version 1.0. (See http://www.boost.org/LICENSE_1_0.txt)
      36             : 
      37             :     See http://www.boost.org for updates, documentation, and revision history.
      38             : 
      39             :    (C) Copyright 2006 David Abrahams - http://www.boost.org.
      40             : 
      41             :    See /boost/serialization/export.hpp, /boost/archive/detail/register_archive.hpp,
      42             :    and /boost/serialization/void_cast.hpp for their implementation. Additional details
      43             :    found in other files split across serialization and archive.
      44             : */
      45             : #ifndef CEREAL_DETAILS_POLYMORPHIC_IMPL_HPP_
      46             : #define CEREAL_DETAILS_POLYMORPHIC_IMPL_HPP_
      47             : 
      48             : #include "cereal/details/polymorphic_impl_fwd.hpp"
      49             : #include "cereal/details/static_object.hpp"
      50             : #include "cereal/types/memory.hpp"
      51             : #include "cereal/types/string.hpp"
      52             : #include <functional>
      53             : #include <typeindex>
      54             : #include <map>
      55             : #include <limits>
      56             : #include <set>
      57             : #include <stack>
      58             : 
      59             : //! Binds a polymorhic type to all registered archives
      60             : /*! This binds a polymorphic type to all compatible registered archives that
      61             :     have been registered with CEREAL_REGISTER_ARCHIVE.  This must be called
      62             :     after all archives are registered (usually after the archives themselves
      63             :     have been included). */
      64             : #define CEREAL_BIND_TO_ARCHIVES(...)                                     \
      65             :     namespace cereal {                                                   \
      66             :     namespace detail {                                                   \
      67             :     template<>                                                           \
      68             :     struct init_binding<__VA_ARGS__> {                                   \
      69             :         static bind_to_archives<__VA_ARGS__> const & b;                  \
      70             :         static void unused() { (void)b; }                                \
      71             :     };                                                                   \
      72             :     bind_to_archives<__VA_ARGS__> const & init_binding<__VA_ARGS__>::b = \
      73             :         ::cereal::detail::StaticObject<                                  \
      74             :             bind_to_archives<__VA_ARGS__>                                \
      75             :         >::getInstance().bind();                                         \
      76             :     }} /* end namespaces */
      77             : 
      78             : namespace cereal
      79             : {
      80             :   /* Polymorphic casting support */
      81             :   namespace detail
      82             :   {
      83             :     //! Base type for polymorphic void casting
      84             :     /*! Contains functions for casting between registered base and derived types.
      85             : 
      86             :         This is necessary so that cereal can properly cast between polymorphic types
      87             :         even though void pointers are used, which normally have no type information.
      88             :         Runtime type information is used instead to index a compile-time made mapping
      89             :         that can perform the proper cast. In the case of multiple levels of inheritance,
      90             :         cereal will attempt to find the shortest path by using registered relationships to
      91             :         perform the cast.
      92             : 
      93             :         This class will be allocated as a StaticObject and only referenced by pointer,
      94             :         allowing a templated derived version of it to define strongly typed functions
      95             :         that cast between registered base and derived types. */
      96             :     struct PolymorphicCaster
      97             :     {
      98           8 :       PolymorphicCaster() = default;
      99             :       PolymorphicCaster( const PolymorphicCaster & ) = default;
     100             :       PolymorphicCaster & operator=( const PolymorphicCaster & ) = default;
     101             :       PolymorphicCaster( PolymorphicCaster && ) CEREAL_NOEXCEPT {}
     102             :       PolymorphicCaster & operator=( PolymorphicCaster && ) CEREAL_NOEXCEPT { return *this; }
     103           0 :       virtual ~PolymorphicCaster() CEREAL_NOEXCEPT = default;
     104             : 
     105             :       //! Downcasts to the proper derived type
     106             :       virtual void const * downcast( void const * const ptr ) const = 0;
     107             :       //! Upcast to proper base type
     108             :       virtual void * upcast( void * const ptr ) const = 0;
     109             :       //! Upcast to proper base type, shared_ptr version
     110             :       virtual std::shared_ptr<void> upcast( std::shared_ptr<void> const & ptr ) const = 0;
     111             :     };
     112             : 
     113             :     //! Holds registered mappings between base and derived types for casting
     114             :     /*! This will be allocated as a StaticObject and holds a map containing
     115             :         all registered mappings between base and derived types. */
     116          36 :     struct PolymorphicCasters
     117             :     {
     118             :       //! Maps from base type index to a map from derived type index to caster
     119             :       std::map<std::type_index, std::map<std::type_index, std::vector<PolymorphicCaster const*>>> map;
     120             : 
     121             :       std::multimap<std::type_index, std::type_index> reverseMap;
     122             : 
     123             :       //! Error message used for unregistered polymorphic casts
     124             :       #define UNREGISTERED_POLYMORPHIC_CAST_EXCEPTION(LoadSave)                                                                                                                \
     125             :         throw cereal::Exception("Trying to " #LoadSave " a registered polymorphic type with an unregistered polymorphic cast.\n"                                               \
     126             :                                 "Could not find a path to a base class (" + util::demangle(baseInfo.name()) + ") for type: " + ::cereal::util::demangledName<Derived>() + "\n" \
     127             :                                 "Make sure you either serialize the base class at some point via cereal::base_class or cereal::virtual_base_class.\n"                          \
     128             :                                 "Alternatively, manually register the association with CEREAL_REGISTER_POLYMORPHIC_RELATION.");
     129             : 
     130             :       //! Checks if the mapping object that can perform the upcast or downcast
     131             :       /*! Uses the type index from the base and derived class to find the matching
     132             :           registered caster. If no matching caster exists, returns false. */
     133          18 :       static bool exists( std::type_index const & baseIndex, std::type_index const & derivedIndex )
     134             :       {
     135             :         // First phase of lookup - match base type index
     136          18 :         auto const & baseMap = StaticObject<PolymorphicCasters>::getInstance().map;
     137          18 :         auto baseIter = baseMap.find( baseIndex );
     138          18 :         if (baseIter == baseMap.end())
     139           0 :           return false;
     140             : 
     141             :         // Second phase - find a match from base to derived
     142          18 :         auto & derivedMap = baseIter->second;
     143          18 :         auto derivedIter = derivedMap.find( derivedIndex );
     144          18 :         if (derivedIter == derivedMap.end())
     145           4 :           return false;
     146             : 
     147          14 :         return true;
     148             :       }
     149             : 
     150             :       //! Gets the mapping object that can perform the upcast or downcast
     151             :       /*! Uses the type index from the base and derived class to find the matching
     152             :           registered caster. If no matching caster exists, calls the exception function.
     153             : 
     154             :           The returned PolymorphicCaster is capable of upcasting or downcasting between the two types. */
     155             :       template <class F> inline
     156        7214 :       static std::vector<PolymorphicCaster const *> const & lookup( std::type_index const & baseIndex, std::type_index const & derivedIndex, F && exceptionFunc )
     157             :       {
     158             :         // First phase of lookup - match base type index
     159        7214 :         auto const & baseMap = StaticObject<PolymorphicCasters>::getInstance().map;
     160        7214 :         auto baseIter = baseMap.find( baseIndex );
     161        7214 :         if( baseIter == baseMap.end() )
     162           0 :           exceptionFunc();
     163             : 
     164             :         // Second phase - find a match from base to derived
     165        7214 :         auto & derivedMap = baseIter->second;
     166        7214 :         auto derivedIter = derivedMap.find( derivedIndex );
     167        7214 :         if( derivedIter == derivedMap.end() )
     168           0 :           exceptionFunc();
     169             : 
     170        7214 :         return derivedIter->second;
     171             :       }
     172             : 
     173             :       //! Performs a downcast to the derived type using a registered mapping
     174             :       template <class Derived> inline
     175        3600 :       static const Derived * downcast( const void * dptr, std::type_info const & baseInfo )
     176             :       {
     177        3600 :         auto const & mapping = lookup( baseInfo, typeid(Derived), [&](){ UNREGISTERED_POLYMORPHIC_CAST_EXCEPTION(save) } );
     178             : 
     179        9600 :         for( auto const * map : mapping )
     180        6000 :           dptr = map->downcast( dptr );
     181             : 
     182        3600 :         return static_cast<Derived const *>( dptr );
     183             :       }
     184             : 
     185             :       //! Performs an upcast to the registered base type using the given a derived type
     186             :       /*! The return is untyped because the final casting to the base type must happen in the polymorphic
     187             :           serialization function, where the type is known at compile time */
     188             :       template <class Derived> inline
     189         800 :       static void * upcast( Derived * const dptr, std::type_info const & baseInfo )
     190             :       {
     191         800 :         auto const & mapping = lookup( baseInfo, typeid(Derived), [&](){ UNREGISTERED_POLYMORPHIC_CAST_EXCEPTION(load) } );
     192             : 
     193         800 :         void * uptr = dptr;
     194        2400 :         for( auto mIter = mapping.rbegin(), mEnd = mapping.rend(); mIter != mEnd; ++mIter )
     195        1600 :           uptr = (*mIter)->upcast( uptr );
     196             : 
     197         800 :         return uptr;
     198             :       }
     199             : 
     200             :       //! Upcasts for shared pointers
     201             :       template <class Derived> inline
     202        2800 :       static std::shared_ptr<void> upcast( std::shared_ptr<Derived> const & dptr, std::type_info const & baseInfo )
     203             :       {
     204        2800 :         auto const & mapping = lookup( baseInfo, typeid(Derived), [&](){ UNREGISTERED_POLYMORPHIC_CAST_EXCEPTION(load) } );
     205             : 
     206        2800 :         std::shared_ptr<void> uptr = dptr;
     207        7200 :         for( auto mIter = mapping.rbegin(), mEnd = mapping.rend(); mIter != mEnd; ++mIter )
     208        4400 :           uptr = (*mIter)->upcast( uptr );
     209             : 
     210        2800 :         return uptr;
     211             :       }
     212             : 
     213             :       #undef UNREGISTERED_POLYMORPHIC_CAST_EXCEPTION
     214             :     };
     215             : 
     216             :     //! Strongly typed derivation of PolymorphicCaster
     217             :     template <class Base, class Derived>
     218           0 :     struct PolymorphicVirtualCaster : PolymorphicCaster
     219             :     {
     220             :       //! Inserts an entry in the polymorphic casting map for this pairing
     221             :       /*! Creates an explicit mapping between Base and Derived in both upwards and
     222             :           downwards directions, allowing void pointers to either to be properly cast
     223             :           assuming dynamic type information is available */
     224           8 :       PolymorphicVirtualCaster()
     225           8 :       {
     226           8 :         const auto baseKey = std::type_index(typeid(Base));
     227           8 :         const auto derivedKey = std::type_index(typeid(Derived));
     228             : 
     229             :         // First insert the relation Base->Derived
     230          16 :         const auto lock = StaticObject<PolymorphicCasters>::lock();
     231           8 :         auto & baseMap = StaticObject<PolymorphicCasters>::getInstance().map;
     232           8 :         auto lb = baseMap.lower_bound(baseKey);
     233             : 
     234             :         {
     235           8 :           auto & derivedMap = baseMap.insert( lb, {baseKey, {}} )->second;
     236           8 :           auto lbd = derivedMap.lower_bound(derivedKey);
     237           8 :           auto & derivedVec = derivedMap.insert( lbd, { std::move(derivedKey), {}} )->second;
     238           8 :           derivedVec.push_back( this );
     239             :         }
     240             : 
     241             :         // Insert reverse relation Derived->Base
     242           8 :         auto & reverseMap = StaticObject<PolymorphicCasters>::getInstance().reverseMap;
     243           8 :         reverseMap.insert( {derivedKey, baseKey} );
     244             : 
     245             :         // Find all chainable unregistered relations
     246             :         /* The strategy here is to process only the nodes in the class hierarchy graph that have been
     247             :            affected by the new insertion. The aglorithm iteratively processes a node an ensures that it
     248             :            is updated with all new shortest length paths. It then rocesses the parents of the active node,
     249             :            with the knowledge that all children have already been processed.
     250             : 
     251             :            Note that for the following, we'll use the nomenclature of parent and child to not confuse with
     252             :            the inserted base derived relationship */
     253             :         {
     254             :           // Checks whether there is a path from parent->child and returns a <dist, path> pair
     255             :           // dist is set to MAX if the path does not exist
     256           0 :           auto checkRelation = [](std::type_index const & parentInfo, std::type_index const & childInfo) ->
     257          18 :             std::pair<size_t, std::vector<PolymorphicCaster const *>>
     258             :           {
     259          18 :             if( PolymorphicCasters::exists( parentInfo, childInfo ) )
     260             :             {
     261          14 :               auto const & path = PolymorphicCasters::lookup( parentInfo, childInfo, [](){} );
     262          14 :               return {path.size(), path};
     263             :             }
     264             :             else
     265           4 :               return {std::numeric_limits<size_t>::max(), {}};
     266             :           };
     267             : 
     268          16 :           std::stack<std::type_index> parentStack;      // Holds the parent nodes to be processed
     269          16 :           std::set<std::type_index>   dirtySet;         // Marks child nodes that have been changed
     270          16 :           std::set<std::type_index>   processedParents; // Marks parent nodes that have been processed
     271             : 
     272             :           // Begin processing the base key and mark derived as dirty
     273           8 :           parentStack.push( baseKey );
     274           8 :           dirtySet.insert( derivedKey );
     275             : 
     276          28 :           while( !parentStack.empty() )
     277             :           {
     278             :             using Relations = std::multimap<std::type_index, std::pair<std::type_index, std::vector<PolymorphicCaster const *>>>;
     279          20 :             Relations unregisteredRelations; // Defer insertions until after main loop to prevent iterator invalidation
     280             : 
     281          10 :             const auto parent = parentStack.top();
     282          10 :             parentStack.pop();
     283             : 
     284             :             // Update paths to all children marked dirty
     285          24 :             for( auto const & childPair : baseMap[parent] )
     286             :             {
     287          14 :               const auto child = childPair.first;
     288          14 :               if( dirtySet.count( child ) && baseMap.count( child ) )
     289             :               {
     290           8 :                 auto parentChildPath = checkRelation( parent, child );
     291             : 
     292             :                 // Search all paths from the child to its own children (finalChild),
     293             :                 // looking for a shorter parth from parent to finalChild
     294          11 :                 for( auto const & finalChildPair : baseMap[child] )
     295             :                 {
     296           7 :                   const auto finalChild = finalChildPair.first;
     297             : 
     298          14 :                   auto parentFinalChildPath = checkRelation( parent, finalChild );
     299          14 :                   auto childFinalChildPath  = checkRelation( child, finalChild );
     300             : 
     301           7 :                   const size_t newLength = 1u + parentChildPath.first;
     302             : 
     303           7 :                   if( newLength < parentFinalChildPath.first )
     304             :                   {
     305          10 :                     std::vector<PolymorphicCaster const *> path = parentChildPath.second;
     306           5 :                     path.insert( path.end(), childFinalChildPath.second.begin(), childFinalChildPath.second.end() );
     307             : 
     308             :                     // Check to see if we have a previous uncommitted path in unregisteredRelations
     309             :                     // that is shorter. If so, ignore this path
     310           5 :                     auto hintRange = unregisteredRelations.equal_range( parent );
     311           5 :                     auto hint = hintRange.first;
     312           9 :                     for( ; hint != hintRange.second; ++hint )
     313           2 :                       if( hint->second.first == finalChild )
     314           0 :                         break;
     315             : 
     316           5 :                     const bool uncommittedExists = hint != unregisteredRelations.end();
     317           5 :                     if( uncommittedExists && (hint->second.second.size() <= newLength) )
     318           0 :                       continue;
     319             : 
     320          10 :                     auto newPath = std::pair<std::type_index, std::vector<PolymorphicCaster const *>>{finalChild, std::move(path)};
     321             : 
     322             :                     // Insert the new path if it doesn't exist, otherwise this will just lookup where to do the
     323             :                     // replacement
     324             :                     #ifdef CEREAL_OLDER_GCC
     325             :                     auto old = unregisteredRelations.insert( hint, std::make_pair(parent, newPath) );
     326             :                     #else // NOT CEREAL_OLDER_GCC
     327           5 :                     auto old = unregisteredRelations.emplace_hint( hint, parent, newPath );
     328             :                     #endif // NOT CEREAL_OLDER_GCC
     329             : 
     330             :                     // If there was an uncommitted path, we need to perform a replacement
     331           5 :                     if( uncommittedExists )
     332           0 :                       old->second = newPath;
     333             :                   }
     334             :                 } // end loop over child's children
     335             :               } // end if dirty and child has children
     336             :             } // end loop over children
     337             : 
     338             :             // Insert chained relations
     339          15 :             for( auto const & it : unregisteredRelations )
     340             :             {
     341           5 :               auto & derivedMap = baseMap.find( it.first )->second;
     342           5 :               derivedMap[it.second.first] = it.second.second;
     343           5 :               reverseMap.insert( {it.second.first, it.first} );
     344             :             }
     345             : 
     346             :             // Mark current parent as modified
     347          10 :             dirtySet.insert( parent );
     348             : 
     349             :             // Insert all parents of the current parent node that haven't yet been processed
     350          10 :             auto parentRange = reverseMap.equal_range( parent );
     351          12 :             for( auto pIter = parentRange.first; pIter != parentRange.second; ++pIter )
     352             :             {
     353           2 :               const auto pParent = pIter->second;
     354           2 :               if( !processedParents.count( pParent ) )
     355             :               {
     356           2 :                 parentStack.push( pParent );
     357           2 :                 processedParents.insert( pParent );
     358             :               }
     359             :             }
     360             :           } // end loop over parent stack
     361             :         } // end chainable relations
     362           8 :       } // end PolymorphicVirtualCaster()
     363             : 
     364             :       //! Performs the proper downcast with the templated types
     365        6000 :       void const * downcast( void const * const ptr ) const override
     366             :       {
     367        6000 :         return dynamic_cast<Derived const*>( static_cast<Base const*>( ptr ) );
     368             :       }
     369             : 
     370             :       //! Performs the proper upcast with the templated types
     371        1600 :       void * upcast( void * const ptr ) const override
     372             :       {
     373        1600 :         return dynamic_cast<Base*>( static_cast<Derived*>( ptr ) );
     374             :       }
     375             : 
     376             :       //! Performs the proper upcast with the templated types (shared_ptr version)
     377        4400 :       std::shared_ptr<void> upcast( std::shared_ptr<void> const & ptr ) const override
     378             :       {
     379        4400 :         return std::dynamic_pointer_cast<Base>( std::static_pointer_cast<Derived>( ptr ) );
     380             :       }
     381             :     };
     382             : 
     383             :     //! Registers a polymorphic casting relation between a Base and Derived type
     384             :     /*! Registering a relation allows cereal to properly cast between the two types
     385             :         given runtime type information and void pointers.
     386             : 
     387             :         Registration happens automatically via cereal::base_class and cereal::virtual_base_class
     388             :         instantiations. For cases where neither is called, see the CEREAL_REGISTER_POLYMORPHIC_RELATION
     389             :         macro */
     390             :     template <class Base, class Derived>
     391             :     struct RegisterPolymorphicCaster
     392             :     {
     393        8000 :       static PolymorphicCaster const * bind( std::true_type /* is_polymorphic<Base> */)
     394             :       {
     395        8000 :         return &StaticObject<PolymorphicVirtualCaster<Base, Derived>>::getInstance();
     396             :       }
     397             : 
     398             :       static PolymorphicCaster const * bind( std::false_type /* is_polymorphic<Base> */ )
     399             :       { return nullptr; }
     400             : 
     401             :       //! Performs registration (binding) between Base and Derived
     402             :       /*! If the type is not polymorphic, nothing will happen */
     403        8000 :       static PolymorphicCaster const * bind()
     404        8000 :       { return bind( typename std::is_polymorphic<Base>::type() ); }
     405             :     };
     406             :   }
     407             : 
     408             :   /* General polymorphism support */
     409             :   namespace detail
     410             :   {
     411             :     //! Binds a compile time type with a user defined string
     412             :     template <class T>
     413             :     struct binding_name {};
     414             : 
     415             :     //! A structure holding a map from type_indices to output serializer functions
     416             :     /*! A static object of this map should be created for each registered archive
     417             :         type, containing entries for every registered type that describe how to
     418             :         properly cast the type to its real type in polymorphic scenarios for
     419             :         shared_ptr, weak_ptr, and unique_ptr. */
     420             :     template <class Archive>
     421           8 :     struct OutputBindingMap
     422             :     {
     423             :       //! A serializer function
     424             :       /*! Serializer functions return nothing and take an archive as
     425             :           their first parameter (will be cast properly inside the function,
     426             :           a pointer to actual data (contents of smart_ptr's get() function)
     427             :           as their second parameter, and the type info of the owning smart_ptr
     428             :           as their final parameter */
     429             :       typedef std::function<void(void*, void const *, std::type_info const &)> Serializer;
     430             : 
     431             :       //! Struct containing the serializer functions for all pointer types
     432          80 :       struct Serializers
     433             :       {
     434             :         Serializer shared_ptr, //!< Serializer function for shared/weak pointers
     435             :                    unique_ptr; //!< Serializer function for unique pointers
     436             :       };
     437             : 
     438             :       //! A map of serializers for pointers of all registered types
     439             :       std::map<std::type_index, Serializers> map;
     440             :     };
     441             : 
     442             :     //! An empty noop deleter
     443         800 :     template<class T> struct EmptyDeleter { void operator()(T *) const {} };
     444             : 
     445             :     //! A structure holding a map from type name strings to input serializer functions
     446             :     /*! A static object of this map should be created for each registered archive
     447             :         type, containing entries for every registered type that describe how to
     448             :         properly cast the type to its real type in polymorphic scenarios for
     449             :         shared_ptr, weak_ptr, and unique_ptr. */
     450             :     template <class Archive>
     451           8 :     struct InputBindingMap
     452             :     {
     453             :       //! Shared ptr serializer function
     454             :       /*! Serializer functions return nothing and take an archive as
     455             :           their first parameter (will be cast properly inside the function,
     456             :           a shared_ptr (or unique_ptr for the unique case) of any base
     457             :           type, and the type id of said base type as the third parameter.
     458             :           Internally it will properly be loaded and cast to the correct type. */
     459             :       typedef std::function<void(void*, std::shared_ptr<void> &, std::type_info const &)> SharedSerializer;
     460             :       //! Unique ptr serializer function
     461             :       typedef std::function<void(void*, std::unique_ptr<void, EmptyDeleter<void>> &, std::type_info const &)> UniqueSerializer;
     462             : 
     463             :       //! Struct containing the serializer functions for all pointer types
     464        7280 :       struct Serializers
     465             :       {
     466             :         SharedSerializer shared_ptr; //!< Serializer function for shared/weak pointers
     467             :         UniqueSerializer unique_ptr; //!< Serializer function for unique pointers
     468             :       };
     469             : 
     470             :       //! A map of serializers for pointers of all registered types
     471             :       std::map<std::string, Serializers> map;
     472             :     };
     473             : 
     474             :     // forward decls for archives from cereal.hpp
     475             :     class InputArchiveBase;
     476             :     class OutputArchiveBase;
     477             : 
     478             :     //! Creates a binding (map entry) between an input archive type and a polymorphic type
     479             :     /*! Bindings are made when types are registered, assuming that at least one
     480             :         archive has already been registered.  When this struct is created,
     481             :         it will insert (at run time) an entry into a map that properly handles
     482             :         casting for serializing polymorphic objects */
     483             :     template <class Archive, class T> struct InputBindingCreator
     484             :     {
     485             :       //! Initialize the binding
     486          16 :       InputBindingCreator()
     487             :       {
     488          16 :         auto & map = StaticObject<InputBindingMap<Archive>>::getInstance().map;
     489          32 :         auto lock = StaticObject<InputBindingMap<Archive>>::lock();
     490          32 :         auto key = std::string(binding_name<T>::name());
     491          16 :         auto lb = map.lower_bound(key);
     492             : 
     493          16 :         if (lb != map.end() && lb->first == key)
     494           0 :           return;
     495             : 
     496          32 :         typename InputBindingMap<Archive>::Serializers serializers;
     497             : 
     498          16 :         serializers.shared_ptr =
     499        2800 :           [](void * arptr, std::shared_ptr<void> & dptr, std::type_info const & baseInfo)
     500             :           {
     501        2800 :             Archive & ar = *static_cast<Archive*>(arptr);
     502        5600 :             std::shared_ptr<T> ptr;
     503             : 
     504        2800 :             ar( CEREAL_NVP_("ptr_wrapper", ::cereal::memory_detail::make_ptr_wrapper(ptr)) );
     505             : 
     506        2800 :             dptr = PolymorphicCasters::template upcast<T>( ptr, baseInfo );
     507        2800 :           };
     508             : 
     509          16 :         serializers.unique_ptr =
     510         800 :           [](void * arptr, std::unique_ptr<void, EmptyDeleter<void>> & dptr, std::type_info const & baseInfo)
     511             :           {
     512         800 :             Archive & ar = *static_cast<Archive*>(arptr);
     513        1600 :             std::unique_ptr<T> ptr;
     514             : 
     515         800 :             ar( CEREAL_NVP_("ptr_wrapper", ::cereal::memory_detail::make_ptr_wrapper(ptr)) );
     516             : 
     517         800 :             dptr.reset( PolymorphicCasters::template upcast<T>( ptr.release(), baseInfo ));
     518         800 :           };
     519             : 
     520          16 :         map.insert( lb, { std::move(key), std::move(serializers) } );
     521             :       }
     522             :     };
     523             : 
     524             :     //! Creates a binding (map entry) between an output archive type and a polymorphic type
     525             :     /*! Bindings are made when types are registered, assuming that at least one
     526             :         archive has already been registered.  When this struct is created,
     527             :         it will insert (at run time) an entry into a map that properly handles
     528             :         casting for serializing polymorphic objects */
     529             :     template <class Archive, class T> struct OutputBindingCreator
     530             :     {
     531             :       //! Writes appropriate metadata to the archive for this polymorphic type
     532        3600 :       static void writeMetadata(Archive & ar)
     533             :       {
     534             :         // Register the polymorphic type name with the archive, and get the id
     535        3600 :         char const * name = binding_name<T>::name();
     536        3600 :         std::uint32_t id = ar.registerPolymorphicType(name);
     537             : 
     538             :         // Serialize the id
     539        3600 :         ar( CEREAL_NVP_("polymorphic_id", id) );
     540             : 
     541             :         // If the msb of the id is 1, then the type name is new, and we should serialize it
     542        3600 :         if( id & detail::msb_32bit )
     543             :         {
     544        3200 :           std::string namestring(name);
     545        1600 :           ar( CEREAL_NVP_("polymorphic_name", namestring) );
     546             :         }
     547        3600 :       }
     548             : 
     549             :       //! Holds a properly typed shared_ptr to the polymorphic type
     550        2800 :       class PolymorphicSharedPointerWrapper
     551             :       {
     552             :         public:
     553             :           /*! Wrap a raw polymorphic pointer in a shared_ptr to its true type
     554             : 
     555             :               The wrapped pointer will not be responsible for ownership of the held pointer
     556             :               so it will not attempt to destroy it; instead the refcount of the wrapped
     557             :               pointer will be tied to a fake 'ownership pointer' that will do nothing
     558             :               when it ultimately goes out of scope.
     559             : 
     560             :               The main reason for doing this, other than not to destroy the true object
     561             :               with our wrapper pointer, is to avoid meddling with the internal reference
     562             :               count in a polymorphic type that inherits from std::enable_shared_from_this.
     563             : 
     564             :               @param dptr A void pointer to the contents of the shared_ptr to serialize */
     565        2800 :           PolymorphicSharedPointerWrapper( T const * dptr ) : refCount(), wrappedPtr( refCount, dptr )
     566        2800 :           { }
     567             : 
     568             :           //! Get the wrapped shared_ptr */
     569        2800 :           inline std::shared_ptr<T const> const & operator()() const { return wrappedPtr; }
     570             : 
     571             :         private:
     572             :           std::shared_ptr<void> refCount;      //!< The ownership pointer
     573             :           std::shared_ptr<T const> wrappedPtr; //!< The wrapped pointer
     574             :       };
     575             : 
     576             :       //! Does the actual work of saving a polymorphic shared_ptr
     577             :       /*! This function will properly create a shared_ptr from the void * that is passed in
     578             :           before passing it to the archive for serialization.
     579             : 
     580             :           In addition, this will also preserve the state of any internal enable_shared_from_this mechanisms
     581             : 
     582             :           @param ar The archive to serialize to
     583             :           @param dptr Pointer to the actual data held by the shared_ptr */
     584         800 :       static inline void savePolymorphicSharedPtr( Archive & ar, T const * dptr, std::true_type /* has_shared_from_this */ )
     585             :       {
     586        1600 :         ::cereal::memory_detail::EnableSharedStateHelper<T> state( const_cast<T *>(dptr) );
     587        1600 :         PolymorphicSharedPointerWrapper psptr( dptr );
     588         800 :         ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper( psptr() ) ) );
     589         800 :       }
     590             : 
     591             :       //! Does the actual work of saving a polymorphic shared_ptr
     592             :       /*! This function will properly create a shared_ptr from the void * that is passed in
     593             :           before passing it to the archive for serialization.
     594             : 
     595             :           This version is for types that do not inherit from std::enable_shared_from_this.
     596             : 
     597             :           @param ar The archive to serialize to
     598             :           @param dptr Pointer to the actual data held by the shared_ptr */
     599        2000 :       static inline void savePolymorphicSharedPtr( Archive & ar, T const * dptr, std::false_type /* has_shared_from_this */ )
     600             :       {
     601        4000 :         PolymorphicSharedPointerWrapper psptr( dptr );
     602        2000 :         ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper( psptr() ) ) );
     603        2000 :       }
     604             : 
     605             :       //! Initialize the binding
     606          16 :       OutputBindingCreator()
     607             :       {
     608          16 :         auto & map = StaticObject<OutputBindingMap<Archive>>::getInstance().map;
     609          16 :         auto key = std::type_index(typeid(T));
     610          16 :         auto lb = map.lower_bound(key);
     611             : 
     612          16 :         if (lb != map.end() && lb->first == key)
     613           0 :           return;
     614             : 
     615          32 :         typename OutputBindingMap<Archive>::Serializers serializers;
     616             : 
     617          16 :         serializers.shared_ptr =
     618        2800 :           [&](void * arptr, void const * dptr, std::type_info const & baseInfo)
     619             :           {
     620        2800 :             Archive & ar = *static_cast<Archive*>(arptr);
     621        2800 :             writeMetadata(ar);
     622             : 
     623        2800 :             auto ptr = PolymorphicCasters::template downcast<T>( dptr, baseInfo );
     624             : 
     625             :             #ifdef _MSC_VER
     626             :             savePolymorphicSharedPtr( ar, ptr, ::cereal::traits::has_shared_from_this<T>::type() ); // MSVC doesn't like typename here
     627             :             #else // not _MSC_VER
     628        2800 :             savePolymorphicSharedPtr( ar, ptr, typename ::cereal::traits::has_shared_from_this<T>::type() );
     629             :             #endif // _MSC_VER
     630        2800 :           };
     631             : 
     632          16 :         serializers.unique_ptr =
     633         800 :           [&](void * arptr, void const * dptr, std::type_info const & baseInfo)
     634             :           {
     635         800 :             Archive & ar = *static_cast<Archive*>(arptr);
     636         800 :             writeMetadata(ar);
     637             : 
     638        1600 :             std::unique_ptr<T const, EmptyDeleter<T const>> const ptr( PolymorphicCasters::template downcast<T>( dptr, baseInfo ) );
     639             : 
     640         800 :             ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper(ptr)) );
     641         800 :           };
     642             : 
     643          16 :         map.insert( { std::move(key), std::move(serializers) } );
     644             :       }
     645             :     };
     646             : 
     647             :     //! Used to help out argument dependent lookup for finding potential overloads
     648             :     //! of instantiate_polymorphic_binding
     649             :     struct adl_tag {};
     650             : 
     651             :     //! Tag for init_binding, bind_to_archives and instantiate_polymorphic_binding. Due to the use of anonymous
     652             :     //! namespace it becomes a different type in each translation unit.
     653             :     namespace { struct polymorphic_binding_tag {}; }
     654             : 
     655             :     //! Causes the static object bindings between an archive type and a serializable type T
     656             :     template <class Archive, class T>
     657             :     struct create_bindings
     658             :     {
     659             :       static const InputBindingCreator<Archive, T> &
     660           0 :       load(std::true_type)
     661             :       {
     662           0 :         return cereal::detail::StaticObject<InputBindingCreator<Archive, T>>::getInstance();
     663             :       }
     664             : 
     665             :       static const OutputBindingCreator<Archive, T> &
     666           0 :       save(std::true_type)
     667             :       {
     668           0 :         return cereal::detail::StaticObject<OutputBindingCreator<Archive, T>>::getInstance();
     669             :       }
     670             : 
     671           0 :       inline static void load(std::false_type) {}
     672           0 :       inline static void save(std::false_type) {}
     673             :     };
     674             : 
     675             :     //! When specialized, causes the compiler to instantiate its parameter
     676             :     template <void(*)()>
     677             :     struct instantiate_function {};
     678             : 
     679             :     /*! This struct is used as the return type of instantiate_polymorphic_binding
     680             :         for specific Archive types.  When the compiler looks for overloads of
     681             :         instantiate_polymorphic_binding, it will be forced to instantiate this
     682             :         struct during overload resolution, even though it will not be part of a valid
     683             :         overload */
     684             :     template <class Archive, class T>
     685             :     struct polymorphic_serialization_support
     686             :     {
     687             :       #if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
     688             :       //! Creates the appropriate bindings depending on whether the archive supports
     689             :       //! saving or loading
     690             :       virtual CEREAL_DLL_EXPORT void instantiate() CEREAL_USED;
     691             :       #else // NOT _MSC_VER
     692             :       //! Creates the appropriate bindings depending on whether the archive supports
     693             :       //! saving or loading
     694             :       static CEREAL_DLL_EXPORT void instantiate() CEREAL_USED;
     695             :       //! This typedef causes the compiler to instantiate this static function
     696             :       typedef instantiate_function<instantiate> unused;
     697             :       #endif // _MSC_VER
     698             :     };
     699             : 
     700             :     // instantiate implementation
     701             :     template <class Archive, class T>
     702           0 :     CEREAL_DLL_EXPORT void polymorphic_serialization_support<Archive,T>::instantiate()
     703             :     {
     704           0 :       create_bindings<Archive,T>::save( std::integral_constant<bool,
     705             :                                           std::is_base_of<detail::OutputArchiveBase, Archive>::value &&
     706             :                                           traits::is_output_serializable<T, Archive>::value>{} );
     707             : 
     708           0 :       create_bindings<Archive,T>::load( std::integral_constant<bool,
     709             :                                           std::is_base_of<detail::InputArchiveBase, Archive>::value &&
     710             :                                           traits::is_input_serializable<T, Archive>::value>{} );
     711           0 :     }
     712             : 
     713             :     //! Begins the binding process of a type to all registered archives
     714             :     /*! Archives need to be registered prior to this struct being instantiated via
     715             :         the CEREAL_REGISTER_ARCHIVE macro.  Overload resolution will then force
     716             :         several static objects to be made that allow us to bind together all
     717             :         registered archive types with the parameter type T. */
     718             :     template <class T, class Tag = polymorphic_binding_tag>
     719             :     struct bind_to_archives
     720             :     {
     721             :       //! Binding for non abstract types
     722           4 :       void bind(std::false_type) const
     723             :       {
     724           4 :         instantiate_polymorphic_binding(static_cast<T*>(nullptr), 0, Tag{}, adl_tag{});
     725           4 :       }
     726             : 
     727             :       //! Binding for abstract types
     728             :       void bind(std::true_type) const
     729             :       { }
     730             : 
     731             :       //! Binds the type T to all registered archives
     732             :       /*! If T is abstract, we will not serialize it and thus
     733             :           do not need to make a binding */
     734           4 :       bind_to_archives const & bind() const
     735             :       {
     736             :         static_assert( std::is_polymorphic<T>::value,
     737             :                        "Attempting to register non polymorphic type" );
     738           4 :         bind( std::is_abstract<T>() );
     739           4 :         return *this;
     740             :       }
     741             :     };
     742             : 
     743             :     //! Used to hide the static object used to bind T to registered archives
     744             :     template <class T, class Tag = polymorphic_binding_tag>
     745             :     struct init_binding;
     746             : 
     747             :     //! Base case overload for instantiation
     748             :     /*! This will end up always being the best overload due to the second
     749             :         parameter always being passed as an int.  All other overloads will
     750             :         accept pointers to archive types and have lower precedence than int.
     751             : 
     752             :         Since the compiler needs to check all possible overloads, the
     753             :         other overloads created via CEREAL_REGISTER_ARCHIVE, which will have
     754             :         lower precedence due to requring a conversion from int to (Archive*),
     755             :         will cause their return types to be instantiated through the static object
     756             :         mechanisms even though they are never called.
     757             : 
     758             :         See the documentation for the other functions to try and understand this */
     759             :     template <class T, typename BindingTag>
     760           4 :     void instantiate_polymorphic_binding( T*, int, BindingTag, adl_tag ) {}
     761             :   } // namespace detail
     762             : } // namespace cereal
     763             : 
     764             : #endif // CEREAL_DETAILS_POLYMORPHIC_IMPL_HPP_

Generated by: LCOV version 1.11