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