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