LCOV - code coverage report
Current view: top level - cereal/details - polymorphic_impl.hpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 177 196 90.3 %
Date: 2022-01-16 21:05:07 Functions: 225 371 60.6 %

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

Generated by: LCOV version 1.14