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