LCOV - code coverage report
Current view: top level - cereal/details - polymorphic_impl.hpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 1141 1571 72.6 %
Date: 2019-10-22 20:09:23 Functions: 168 284 59.2 %

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

Generated by: LCOV version 1.13