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