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