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