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 PolymorphicCaster() = default; 107 PolymorphicCaster( const PolymorphicCaster & ) = default; 108 PolymorphicCaster & operator=( const PolymorphicCaster & ) = default; PolymorphicCastercereal::detail::PolymorphicCaster109 PolymorphicCaster( PolymorphicCaster && ) CEREAL_NOEXCEPT {} operator =cereal::detail::PolymorphicCaster110 PolymorphicCaster & operator=( PolymorphicCaster && ) CEREAL_NOEXCEPT { return *this; } 111 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 &> lookup_if_existscereal::detail::PolymorphicCasters145 lookup_if_exists( std::type_index const & baseIndex, std::type_index const & derivedIndex ) 146 { 147 // First phase of lookup - match base type index 148 auto const & baseMap = StaticObject<PolymorphicCasters>::getInstance().map; 149 auto baseIter = baseMap.find( baseIndex ); 150 if (baseIter == baseMap.end()) 151 return {false, {}}; 152 153 // Second phase - find a match from base to derived 154 auto const & derivedMap = baseIter->second; 155 auto derivedIter = derivedMap.find( derivedIndex ); 156 if (derivedIter == derivedMap.end()) 157 return {false, {}}; 158 159 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 lookupcereal::detail::PolymorphicCasters168 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 auto const & baseMap = StaticObject<PolymorphicCasters>::getInstance().map; 172 auto baseIter = baseMap.find( baseIndex ); 173 if( baseIter == baseMap.end() ) 174 exceptionFunc(); 175 176 // Second phase - find a match from base to derived 177 auto const & derivedMap = baseIter->second; 178 auto derivedIter = derivedMap.find( derivedIndex ); 179 if( derivedIter == derivedMap.end() ) 180 exceptionFunc(); 181 182 return derivedIter->second; 183 } 184 185 //! Performs a downcast to the derived type using a registered mapping 186 template <class Derived> inline downcastcereal::detail::PolymorphicCasters187 static const Derived * downcast( const void * dptr, std::type_info const & baseInfo ) 188 { 189 auto const & mapping = lookup( baseInfo, typeid(Derived), [&](){ UNREGISTERED_POLYMORPHIC_CAST_EXCEPTION(save) } ); 190 191 for( auto const * dmap : mapping ) 192 dptr = dmap->downcast( dptr ); 193 194 return static_cast<Derived const *>( dptr ); 195 } 196 197 //! Performs an upcast to the registered base type using the given a derived type 198 /*! 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 template <class Derived> inline upcastcereal::detail::PolymorphicCasters201 static void * upcast( Derived * const dptr, std::type_info const & baseInfo ) 202 { 203 auto const & mapping = lookup( baseInfo, typeid(Derived), [&](){ UNREGISTERED_POLYMORPHIC_CAST_EXCEPTION(load) } ); 204 205 void * uptr = dptr; 206 for( auto mIter = mapping.rbegin(), mEnd = mapping.rend(); mIter != mEnd; ++mIter ) 207 uptr = (*mIter)->upcast( uptr ); 208 209 return uptr; 210 } 211 212 //! Upcasts for shared pointers 213 template <class Derived> inline upcastcereal::detail::PolymorphicCasters214 static std::shared_ptr<void> upcast( std::shared_ptr<Derived> const & dptr, std::type_info const & baseInfo ) 215 { 216 auto const & mapping = lookup( baseInfo, typeid(Derived), [&](){ UNREGISTERED_POLYMORPHIC_CAST_EXCEPTION(load) } ); 217 218 std::shared_ptr<void> uptr = dptr; 219 for( auto mIter = mapping.rbegin(), mEnd = mapping.rend(); mIter != mEnd; ++mIter ) 220 uptr = (*mIter)->upcast( uptr ); 221 222 return uptr; 223 } 224 225 #undef UNREGISTERED_POLYMORPHIC_CAST_EXCEPTION 226 }; 227 228 #ifdef CEREAL_OLDER_GCC 229 #define CEREAL_EMPLACE_MAP(map, key, value) \ 230 map.insert( std::make_pair(std::move(key), std::move(value)) ); 231 #else // NOT CEREAL_OLDER_GCC 232 #define CEREAL_EMPLACE_MAP(map, key, value) \ 233 map.emplace( key, value ); 234 #endif // NOT_CEREAL_OLDER_GCC 235 236 //! Strongly typed derivation of PolymorphicCaster 237 template <class Base, class Derived> 238 struct PolymorphicVirtualCaster : PolymorphicCaster 239 { 240 //! Inserts an entry in the polymorphic casting map for this pairing 241 /*! Creates an explicit mapping between Base and Derived in both upwards and 242 downwards directions, allowing void pointers to either to be properly cast 243 assuming dynamic type information is available */ PolymorphicVirtualCastercereal::detail::PolymorphicVirtualCaster244 PolymorphicVirtualCaster() 245 { 246 const auto baseKey = std::type_index(typeid(Base)); 247 const auto derivedKey = std::type_index(typeid(Derived)); 248 249 // First insert the relation Base->Derived 250 const auto lock = StaticObject<PolymorphicCasters>::lock(); 251 auto & baseMap = StaticObject<PolymorphicCasters>::getInstance().map; 252 253 { 254 auto & derivedMap = baseMap.insert( {baseKey, PolymorphicCasters::DerivedCasterMap{}} ).first->second; 255 auto & derivedVec = derivedMap.insert( {derivedKey, {}} ).first->second; 256 derivedVec.push_back( this ); 257 } 258 259 // Insert reverse relation Derived->Base 260 auto & reverseMap = StaticObject<PolymorphicCasters>::getInstance().reverseMap; 261 CEREAL_EMPLACE_MAP(reverseMap, derivedKey, baseKey); 262 263 // Find all chainable unregistered relations 264 /* 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 is updated with all new shortest length paths. It then rocesses the parents of the active node, 267 with the knowledge that all children have already been processed. 268 269 Note that for the following, we'll use the nomenclature of parent and child to not confuse with 270 the inserted base derived relationship */ 271 { 272 // Checks whether there is a path from parent->child and returns a <dist, path> pair 273 // dist is set to MAX if the path does not exist 274 auto checkRelation = [](std::type_index const & parentInfo, std::type_index const & childInfo) -> 275 std::pair<size_t, std::vector<PolymorphicCaster const *> const &> 276 { 277 auto result = PolymorphicCasters::lookup_if_exists( parentInfo, childInfo ); 278 if( result.first ) 279 { 280 auto const & path = result.second; 281 return {path.size(), path}; 282 } 283 else 284 return {(std::numeric_limits<size_t>::max)(), {}}; 285 }; 286 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 std::unordered_set<std::type_index> processedParents; // Marks parent nodes that have been processed 290 291 // Checks if a child has been marked dirty 292 auto isDirty = [&](std::type_index const & c) 293 { 294 auto const dirtySetSize = dirtySet.size(); 295 for( size_t i = 0; i < dirtySetSize; ++i ) 296 if( dirtySet[i] == c ) 297 return true; 298 299 return false; 300 }; 301 302 // Begin processing the base key and mark derived as dirty 303 parentStack.push( baseKey ); 304 dirtySet.emplace_back( derivedKey ); 305 306 while( !parentStack.empty() ) 307 { 308 using Relations = std::unordered_multimap<std::type_index, std::pair<std::type_index, std::vector<PolymorphicCaster const *>>>; 309 Relations unregisteredRelations; // Defer insertions until after main loop to prevent iterator invalidation 310 311 const auto parent = parentStack.top(); 312 parentStack.pop(); 313 314 // Update paths to all children marked dirty 315 for( auto const & childPair : baseMap[parent] ) 316 { 317 const auto child = childPair.first; 318 if( isDirty( child ) && baseMap.count( child ) ) 319 { 320 auto parentChildPath = checkRelation( parent, child ); 321 322 // Search all paths from the child to its own children (finalChild), 323 // looking for a shorter parth from parent to finalChild 324 for( auto const & finalChildPair : baseMap[child] ) 325 { 326 const auto finalChild = finalChildPair.first; 327 328 auto parentFinalChildPath = checkRelation( parent, finalChild ); 329 auto childFinalChildPath = checkRelation( child, finalChild ); 330 331 const size_t newLength = 1u + parentChildPath.first; 332 333 if( newLength < parentFinalChildPath.first ) 334 { 335 std::vector<PolymorphicCaster const *> path = parentChildPath.second; 336 path.insert( path.end(), childFinalChildPath.second.begin(), childFinalChildPath.second.end() ); 337 338 // Check to see if we have a previous uncommitted path in unregisteredRelations 339 // that is shorter. If so, ignore this path 340 auto hintRange = unregisteredRelations.equal_range( parent ); 341 auto hint = hintRange.first; 342 for( ; hint != hintRange.second; ++hint ) 343 if( hint->second.first == finalChild ) 344 break; 345 346 const bool uncommittedExists = hint != unregisteredRelations.end(); 347 if( uncommittedExists && (hint->second.second.size() <= newLength) ) 348 continue; 349 350 auto newPath = std::pair<std::type_index, std::vector<PolymorphicCaster const *>>{finalChild, std::move(path)}; 351 352 // Insert the new path if it doesn't exist, otherwise this will just lookup where to do the 353 // replacement 354 #ifdef CEREAL_OLDER_GCC 355 auto old = unregisteredRelations.insert( hint, std::make_pair(parent, newPath) ); 356 #else // NOT CEREAL_OLDER_GCC 357 auto old = unregisteredRelations.emplace_hint( hint, parent, newPath ); 358 #endif // NOT CEREAL_OLDER_GCC 359 360 // If there was an uncommitted path, we need to perform a replacement 361 if( uncommittedExists ) 362 old->second = newPath; 363 } 364 } // end loop over child's children 365 } // end if dirty and child has children 366 } // end loop over children 367 368 // Insert chained relations 369 for( auto const & it : unregisteredRelations ) 370 { 371 auto & derivedMap = baseMap.find( it.first )->second; 372 derivedMap[it.second.first] = it.second.second; 373 CEREAL_EMPLACE_MAP(reverseMap, it.second.first, it.first ); 374 } 375 376 // Mark current parent as modified 377 dirtySet.emplace_back( parent ); 378 379 // Insert all parents of the current parent node that haven't yet been processed 380 auto parentRange = reverseMap.equal_range( parent ); 381 for( auto pIter = parentRange.first; pIter != parentRange.second; ++pIter ) 382 { 383 const auto pParent = pIter->second; 384 if( !processedParents.count( pParent ) ) 385 { 386 parentStack.push( pParent ); 387 processedParents.insert( pParent ); 388 } 389 } 390 } // end loop over parent stack 391 } // end chainable relations 392 } // end PolymorphicVirtualCaster() 393 394 #undef CEREAL_EMPLACE_MAP 395 396 //! Performs the proper downcast with the templated types downcastcereal::detail::PolymorphicVirtualCaster397 void const * downcast( void const * const ptr ) const override 398 { 399 return dynamic_cast<Derived const*>( static_cast<Base const*>( ptr ) ); 400 } 401 402 //! Performs the proper upcast with the templated types upcastcereal::detail::PolymorphicVirtualCaster403 void * upcast( void * const ptr ) const override 404 { 405 return dynamic_cast<Base*>( static_cast<Derived*>( ptr ) ); 406 } 407 408 //! Performs the proper upcast with the templated types (shared_ptr version) upcastcereal::detail::PolymorphicVirtualCaster409 std::shared_ptr<void> upcast( std::shared_ptr<void> const & ptr ) const override 410 { 411 return std::dynamic_pointer_cast<Base>( std::static_pointer_cast<Derived>( ptr ) ); 412 } 413 }; 414 415 //! 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 given runtime type information and void pointers. 418 419 Registration happens automatically via cereal::base_class and cereal::virtual_base_class 420 instantiations. For cases where neither is called, see the CEREAL_REGISTER_POLYMORPHIC_RELATION 421 macro */ 422 template <class Base, class Derived> 423 struct RegisterPolymorphicCaster 424 { bindcereal::detail::RegisterPolymorphicCaster425 static PolymorphicCaster const * bind( std::true_type /* is_polymorphic<Base> */) 426 { 427 return &StaticObject<PolymorphicVirtualCaster<Base, Derived>>::getInstance(); 428 } 429 bindcereal::detail::RegisterPolymorphicCaster430 static PolymorphicCaster const * bind( std::false_type /* is_polymorphic<Base> */ ) 431 { return nullptr; } 432 433 //! Performs registration (binding) between Base and Derived 434 /*! If the type is not polymorphic, nothing will happen */ bindcereal::detail::RegisterPolymorphicCaster435 static PolymorphicCaster const * bind() 436 { return bind( typename std::is_polymorphic<Base>::type() ); } 437 }; 438 } 439 440 /* General polymorphism support */ 441 namespace detail 442 { 443 //! Binds a compile time type with a user defined string 444 template <class T> 445 struct binding_name {}; 446 447 //! 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 type, containing entries for every registered type that describe how to 450 properly cast the type to its real type in polymorphic scenarios for 451 shared_ptr, weak_ptr, and unique_ptr. */ 452 template <class Archive> 453 struct OutputBindingMap 454 { 455 //! A serializer function 456 /*! Serializer functions return nothing and take an archive as 457 their first parameter (will be cast properly inside the function, 458 a pointer to actual data (contents of smart_ptr's get() function) 459 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 operator ()cereal::detail::EmptyDeleter475 template<class T> struct EmptyDeleter { void operator()(T *) const {} }; 476 477 //! A structure holding a map from type name strings to input serializer functions 478 /*! 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 shared_ptr, weak_ptr, and unique_ptr. */ 482 template <class Archive> 483 struct InputBindingMap 484 { 485 //! Shared ptr serializer function 486 /*! Serializer functions return nothing and take an archive as 487 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 typedef std::function<void(void*, std::shared_ptr<void> &, std::type_info const &)> SharedSerializer; 492 //! 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 // forward decls for archives from cereal.hpp 507 class InputArchiveBase; 508 class OutputArchiveBase; 509 510 //! Creates a binding (map entry) between an input archive type and a polymorphic type 511 /*! Bindings are made when types are registered, assuming that at least one 512 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 template <class Archive, class T> struct InputBindingCreator 516 { 517 //! Initialize the binding InputBindingCreatorcereal::detail::InputBindingCreator518 InputBindingCreator() 519 { 520 auto & map = StaticObject<InputBindingMap<Archive>>::getInstance().map; 521 auto lock = StaticObject<InputBindingMap<Archive>>::lock(); 522 auto key = std::string(binding_name<T>::name()); 523 auto lb = map.lower_bound(key); 524 525 if (lb != map.end() && lb->first == key) 526 return; 527 528 typename InputBindingMap<Archive>::Serializers serializers; 529 530 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 std::shared_ptr<T> ptr; 535 536 ar( CEREAL_NVP_("ptr_wrapper", ::cereal::memory_detail::make_ptr_wrapper(ptr)) ); 537 538 dptr = PolymorphicCasters::template upcast<T>( ptr, baseInfo ); 539 }; 540 541 serializers.unique_ptr = 542 [](void * arptr, std::unique_ptr<void, EmptyDeleter<void>> & dptr, std::type_info const & baseInfo) 543 { 544 Archive & ar = *static_cast<Archive*>(arptr); 545 std::unique_ptr<T> ptr; 546 547 ar( CEREAL_NVP_("ptr_wrapper", ::cereal::memory_detail::make_ptr_wrapper(ptr)) ); 548 549 dptr.reset( PolymorphicCasters::template upcast<T>( ptr.release(), baseInfo )); 550 }; 551 552 map.insert( lb, { std::move(key), std::move(serializers) } ); 553 } 554 }; 555 556 //! Creates a binding (map entry) between an output archive type and a polymorphic type 557 /*! Bindings are made when types are registered, assuming that at least one 558 archive has already been registered. When this struct is created, 559 it will insert (at run time) an entry into a map that properly handles 560 casting for serializing polymorphic objects */ 561 template <class Archive, class T> struct OutputBindingCreator 562 { 563 //! Writes appropriate metadata to the archive for this polymorphic type writeMetadatacereal::detail::OutputBindingCreator564 static void writeMetadata(Archive & ar) 565 { 566 // Register the polymorphic type name with the archive, and get the id 567 char const * name = binding_name<T>::name(); 568 std::uint32_t id = ar.registerPolymorphicType(name); 569 570 // Serialize the id 571 ar( CEREAL_NVP_("polymorphic_id", id) ); 572 573 // If the msb of the id is 1, then the type name is new, and we should serialize it 574 if( id & detail::msb_32bit ) 575 { 576 std::string namestring(name); 577 ar( CEREAL_NVP_("polymorphic_name", namestring) ); 578 } 579 } 580 581 //! Holds a properly typed shared_ptr to the polymorphic type 582 class PolymorphicSharedPointerWrapper 583 { 584 public: 585 /*! Wrap a raw polymorphic pointer in a shared_ptr to its true type 586 587 The wrapped pointer will not be responsible for ownership of the held pointer 588 so it will not attempt to destroy it; instead the refcount of the wrapped 589 pointer will be tied to a fake 'ownership pointer' that will do nothing 590 when it ultimately goes out of scope. 591 592 The main reason for doing this, other than not to destroy the true object 593 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 596 @param dptr A void pointer to the contents of the shared_ptr to serialize */ PolymorphicSharedPointerWrapper(T const * dptr)597 PolymorphicSharedPointerWrapper( T const * dptr ) : refCount(), wrappedPtr( refCount, dptr ) 598 { } 599 600 //! Get the wrapped shared_ptr */ operator ()() const601 inline std::shared_ptr<T const> const & operator()() const { return wrappedPtr; } 602 603 private: 604 std::shared_ptr<void> refCount; //!< The ownership pointer 605 std::shared_ptr<T const> wrappedPtr; //!< The wrapped pointer 606 }; 607 608 //! Does the actual work of saving a polymorphic shared_ptr 609 /*! This function will properly create a shared_ptr from the void * that is passed in 610 before passing it to the archive for serialization. 611 612 In addition, this will also preserve the state of any internal enable_shared_from_this mechanisms 613 614 @param ar The archive to serialize to 615 @param dptr Pointer to the actual data held by the shared_ptr */ savePolymorphicSharedPtrcereal::detail::OutputBindingCreator616 static inline void savePolymorphicSharedPtr( Archive & ar, T const * dptr, std::true_type /* has_shared_from_this */ ) 617 { 618 ::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 //! 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 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 @param dptr Pointer to the actual data held by the shared_ptr */ savePolymorphicSharedPtrcereal::detail::OutputBindingCreator631 static inline void savePolymorphicSharedPtr( Archive & ar, T const * dptr, std::false_type /* has_shared_from_this */ ) 632 { 633 PolymorphicSharedPointerWrapper psptr( dptr ); 634 ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper( psptr() ) ) ); 635 } 636 637 //! Initialize the binding OutputBindingCreatorcereal::detail::OutputBindingCreator638 OutputBindingCreator() 639 { 640 auto & map = StaticObject<OutputBindingMap<Archive>>::getInstance().map; 641 auto key = std::type_index(typeid(T)); 642 auto lb = map.lower_bound(key); 643 644 if (lb != map.end() && lb->first == key) 645 return; 646 647 typename OutputBindingMap<Archive>::Serializers serializers; 648 649 serializers.shared_ptr = 650 [&](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 #endif // _MSC_VER 662 }; 663 664 serializers.unique_ptr = 665 [&](void * arptr, void const * dptr, std::type_info const & baseInfo) 666 { 667 Archive & ar = *static_cast<Archive*>(arptr); 668 writeMetadata(ar); 669 670 std::unique_ptr<T const, EmptyDeleter<T const>> const ptr( PolymorphicCasters::template downcast<T>( dptr, baseInfo ) ); 671 672 ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper(ptr)) ); 673 }; 674 675 map.insert( { std::move(key), std::move(serializers) } ); 676 } 677 }; 678 679 //! Used to help out argument dependent lookup for finding potential overloads 680 //! of instantiate_polymorphic_binding 681 struct adl_tag {}; 682 683 //! Tag for init_binding, bind_to_archives and instantiate_polymorphic_binding. Due to the use of anonymous 684 //! namespace it becomes a different type in each translation unit. 685 namespace { struct polymorphic_binding_tag {}; } 686 687 //! Causes the static object bindings between an archive type and a serializable type T 688 template <class Archive, class T> 689 struct create_bindings 690 { 691 static const InputBindingCreator<Archive, T> & loadcereal::detail::create_bindings692 load(std::true_type) 693 { 694 return cereal::detail::StaticObject<InputBindingCreator<Archive, T>>::getInstance(); 695 } 696 697 static const OutputBindingCreator<Archive, T> & savecereal::detail::create_bindings698 save(std::true_type) 699 { 700 return cereal::detail::StaticObject<OutputBindingCreator<Archive, T>>::getInstance(); 701 } 702 loadcereal::detail::create_bindings703 inline static void load(std::false_type) {} savecereal::detail::create_bindings704 inline static void save(std::false_type) {} 705 }; 706 707 //! When specialized, causes the compiler to instantiate its parameter 708 template <void(*)()> 709 struct instantiate_function {}; 710 711 /*! This struct is used as the return type of instantiate_polymorphic_binding 712 for specific Archive types. When the compiler looks for overloads of 713 instantiate_polymorphic_binding, it will be forced to instantiate this 714 struct during overload resolution, even though it will not be part of a valid 715 overload */ 716 template <class Archive, class T> 717 struct polymorphic_serialization_support 718 { 719 #if defined(_MSC_VER) && !defined(__INTEL_COMPILER) 720 //! Creates the appropriate bindings depending on whether the archive supports 721 //! saving or loading 722 virtual CEREAL_DLL_EXPORT void instantiate() CEREAL_USED; 723 #else // NOT _MSC_VER 724 //! Creates the appropriate bindings depending on whether the archive supports 725 //! saving or loading 726 static CEREAL_DLL_EXPORT void instantiate() CEREAL_USED; 727 //! This typedef causes the compiler to instantiate this static function 728 typedef instantiate_function<instantiate> unused; 729 #endif // _MSC_VER 730 }; 731 732 // instantiate implementation 733 template <class Archive, class T> instantiate()734 CEREAL_DLL_EXPORT void polymorphic_serialization_support<Archive,T>::instantiate() 735 { 736 create_bindings<Archive,T>::save( std::integral_constant<bool, 737 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 std::is_base_of<detail::InputArchiveBase, Archive>::value && 742 traits::is_input_serializable<T, Archive>::value>{} ); 743 } 744 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 registered archive types with the parameter type T. */ 750 template <class T, class Tag = polymorphic_binding_tag> 751 struct bind_to_archives 752 { 753 //! Binding for non abstract types bindcereal::detail::bind_to_archives754 void bind(std::false_type) const 755 { 756 instantiate_polymorphic_binding(static_cast<T*>(nullptr), 0, Tag{}, adl_tag{}); 757 } 758 759 //! Binding for abstract types bindcereal::detail::bind_to_archives760 void bind(std::true_type) const 761 { } 762 763 //! Binds the type T to all registered archives 764 /*! If T is abstract, we will not serialize it and thus 765 do not need to make a binding */ bindcereal::detail::bind_to_archives766 bind_to_archives const & bind() const 767 { 768 static_assert( std::is_polymorphic<T>::value, 769 "Attempting to register non polymorphic type" ); 770 bind( std::is_abstract<T>() ); 771 return *this; 772 } 773 }; 774 775 //! Used to hide the static object used to bind T to registered archives 776 template <class T, class Tag = polymorphic_binding_tag> 777 struct init_binding; 778 779 //! Base case overload for instantiation 780 /*! 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 accept pointers to archive types and have lower precedence than int. 783 784 Since the compiler needs to check all possible overloads, the 785 other overloads created via CEREAL_REGISTER_ARCHIVE, which will have 786 lower precedence due to requring a conversion from int to (Archive*), 787 will cause their return types to be instantiated through the static object 788 mechanisms even though they are never called. 789 790 See the documentation for the other functions to try and understand this */ 791 template <class T, typename BindingTag> instantiate_polymorphic_binding(T *,int,BindingTag,adl_tag)792 void instantiate_polymorphic_binding( T*, int, BindingTag, adl_tag ) {} 793 } // namespace detail 794 } // namespace cereal 795 796 #endif // CEREAL_DETAILS_POLYMORPHIC_IMPL_HPP_ 797