1 /*! \file polymorphic.hpp
2     \brief Support for pointers to polymorphic base classes
3     \ingroup OtherTypes */
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 #ifndef CEREAL_TYPES_POLYMORPHIC_HPP_
31 #define CEREAL_TYPES_POLYMORPHIC_HPP_
32 
33 #include "cereal/cereal.hpp"
34 #include "cereal/types/memory.hpp"
35 
36 #include "cereal/details/util.hpp"
37 #include "cereal/details/helpers.hpp"
38 #include "cereal/details/traits.hpp"
39 #include "cereal/details/polymorphic_impl.hpp"
40 
41 #ifdef _MSC_VER
42 #define CEREAL_STATIC_CONSTEXPR static
43 #else
44 #define CEREAL_STATIC_CONSTEXPR static constexpr
45 #endif
46 
47 //! Registers a derived polymorphic type with cereal
48 /*! Polymorphic types must be registered before smart
49     pointers to them can be serialized.  Note that base
50     classes do not need to be registered.
51 
52     Registering a type lets cereal know how to properly
53     serialize it when a smart pointer to a base object is
54     used in conjunction with a derived class.
55 
56     This assumes that all relevant archives have also
57     previously been registered.  Registration for archives
58     is usually done in the header file in which they are
59     defined.  This means that type registration needs to
60     happen after specific archives to be used are included.
61 
62     It is recommended that type registration be done in
63     the header file in which the type is declared.
64 
65     Registration can also be placed in a source file,
66     but this may require the use of the
67     CEREAL_REGISTER_DYNAMIC_INIT macro (see below).
68 
69     Registration may be called repeatedly for the same
70     type in different translation units to add support
71     for additional archives if they are not initially
72     available (included and registered).
73 
74     When building serialization support as a DLL on
75     Windows, registration must happen in the header file.
76     On Linux and Mac things should still work properly
77     if placed in a source file, but see the above comments
78     on registering in source files.
79 
80     Polymorphic support in cereal requires RTTI to be
81     enabled */
82 #define CEREAL_REGISTER_TYPE(...)                                        \
83   namespace cereal {                                                     \
84   namespace detail {                                                     \
85   template <>                                                            \
86   struct binding_name<__VA_ARGS__>                                       \
87   {                                                                      \
88     CEREAL_STATIC_CONSTEXPR char const * name() { return #__VA_ARGS__; } \
89   };                                                                     \
90   } } /* end namespaces */                                               \
91   CEREAL_BIND_TO_ARCHIVES(__VA_ARGS__)
92 
93 //! Registers a polymorphic type with cereal, giving it a
94 //! user defined name
95 /*! In some cases the default name used with
96     CEREAL_REGISTER_TYPE (the name of the type) may not be
97     suitable.  This macro allows any name to be associated
98     with the type.  The name should be unique */
99 #define CEREAL_REGISTER_TYPE_WITH_NAME(T, Name)                     \
100   namespace cereal {                                                \
101   namespace detail {                                                \
102   template <>                                                       \
103   struct binding_name<T>                                            \
104   { CEREAL_STATIC_CONSTEXPR char const * name() { return Name; } }; \
105   } } /* end namespaces */                                          \
106   CEREAL_BIND_TO_ARCHIVES(T)
107 
108 //! Registers the base-derived relationship for a polymorphic type
109 /*! When polymorphic serialization occurs, cereal needs to know how to
110     properly cast between derived and base types for the polymorphic
111     type. Normally this happens automatically whenever cereal::base_class
112     or cereal::virtual_base_class are used to serialize a base class. In
113     cases where neither of these is ever called but a base class still
114     exists, this explicit registration is required.
115 
116     The Derived class should be the most derived type that will be serialized,
117     and the Base type any possible base that has not been covered under a base
118     class serialization that will be used to store a Derived pointer.
119 
120     Placement of this is the same as for CEREAL_REGISTER_TYPE. */
121 #define CEREAL_REGISTER_POLYMORPHIC_RELATION(Base, Derived)                     \
122   namespace cereal {                                                            \
123   namespace detail {                                                            \
124   template <>                                                                   \
125   struct PolymorphicRelation<Base, Derived>                                     \
126   { static void bind() { RegisterPolymorphicCaster<Base, Derived>::bind(); } }; \
127   } } /* end namespaces */
128 
129 //! Adds a way to force initialization of a translation unit containing
130 //! calls to CEREAL_REGISTER_TYPE
131 /*! In C++, dynamic initialization of non-local variables of a translation
132     unit may be deferred until "the first odr-use of any function or variable
133     defined in the same translation unit as the variable to be initialized."
134 
135     Informally, odr-use means that your program takes the address of or binds
136     a reference directly to an object, which must have a definition.
137 
138     Since polymorphic type support in cereal relies on the dynamic
139     initialization of certain global objects happening before
140     serialization is performed, it is important to ensure that something
141     from files that call CEREAL_REGISTER_TYPE is odr-used before serialization
142     occurs, otherwise the registration will never take place.  This may often
143     be the case when serialization is built as a shared library external from
144     your main program.
145 
146     This macro, with any name of your choosing, should be placed into the
147     source file that contains calls to CEREAL_REGISTER_TYPE.
148 
149     Its counterpart, CEREAL_FORCE_DYNAMIC_INIT, should be placed in its
150     associated header file such that it is included in the translation units
151     (source files) in which you want the registration to appear.
152 
153     @relates CEREAL_FORCE_DYNAMIC_INIT
154     */
155 #define CEREAL_REGISTER_DYNAMIC_INIT(LibName)                \
156   namespace cereal {                                         \
157   namespace detail {                                         \
158     void CEREAL_DLL_EXPORT dynamic_init_dummy_##LibName() {} \
159   } } /* end namespaces */
160 
161 //! Forces dynamic initialization of polymorphic support in a
162 //! previously registered source file
163 /*! @sa CEREAL_REGISTER_DYNAMIC_INIT
164 
165     See CEREAL_REGISTER_DYNAMIC_INIT for detailed explanation
166     of how this macro should be used.  The name used should
167     match that for CEREAL_REGISTER_DYNAMIC_INIT. */
168 #define CEREAL_FORCE_DYNAMIC_INIT(LibName)                 \
169   namespace cereal {                                       \
170   namespace detail {                                       \
171     void CEREAL_DLL_EXPORT dynamic_init_dummy_##LibName(); \
172   } /* end detail */                                       \
173   } /* end cereal */                                       \
174   namespace {                                              \
175     struct dynamic_init_##LibName {                        \
176       dynamic_init_##LibName() {                           \
177         ::cereal::detail::dynamic_init_dummy_##LibName();  \
178       }                                                    \
179     } dynamic_init_instance_##LibName;                     \
180   } /* end anonymous namespace */
181 
182 namespace cereal
183 {
184   namespace polymorphic_detail
185   {
186     //! Error message used for unregistered polymorphic types
187     /*! @internal */
188     #define UNREGISTERED_POLYMORPHIC_EXCEPTION(LoadSave, Name)                                                                                      \
189       throw cereal::Exception("Trying to " #LoadSave " an unregistered polymorphic type (" + Name + ").\n"                                          \
190                               "Make sure your type is registered with CEREAL_REGISTER_TYPE and that the archive "                                   \
191                               "you are using was included (and registered with CEREAL_REGISTER_ARCHIVE) prior to calling CEREAL_REGISTER_TYPE.\n"   \
192                               "If your type is already registered and you still see this error, you may need to use CEREAL_REGISTER_DYNAMIC_INIT.");
193 
194     //! Get an input binding from the given archive by deserializing the type meta data
195     /*! @internal */
196     template<class Archive> inline
getInputBinding(Archive & ar,std::uint32_t const nameid)197     typename ::cereal::detail::InputBindingMap<Archive>::Serializers getInputBinding(Archive & ar, std::uint32_t const nameid)
198     {
199       // If the nameid is zero, we serialized a null pointer
200       if(nameid == 0)
201       {
202         typename ::cereal::detail::InputBindingMap<Archive>::Serializers emptySerializers;
203         emptySerializers.shared_ptr = [](void*, std::shared_ptr<void> & ptr, std::type_info const &) { ptr.reset(); };
204         emptySerializers.unique_ptr = [](void*, std::unique_ptr<void, ::cereal::detail::EmptyDeleter<void>> & ptr, std::type_info const &) { ptr.reset( nullptr ); };
205         return emptySerializers;
206       }
207 
208       std::string name;
209       if(nameid & detail::msb_32bit)
210       {
211         ar( CEREAL_NVP_("polymorphic_name", name) );
212         ar.registerPolymorphicName(nameid, name);
213       }
214       else
215         name = ar.getPolymorphicName(nameid);
216 
217       auto const & bindingMap = detail::StaticObject<detail::InputBindingMap<Archive>>::getInstance().map;
218 
219       auto binding = bindingMap.find(name);
220       if(binding == bindingMap.end())
221         UNREGISTERED_POLYMORPHIC_EXCEPTION(load, name)
222       return binding->second;
223     }
224 
225     //! Serialize a shared_ptr if the 2nd msb in the nameid is set, and if we can actually construct the pointee
226     /*! This check lets us try and skip doing polymorphic machinery if we can get away with
227         using the derived class serialize function
228 
229         Note that on MSVC 2013 preview, is_default_constructible<T> returns true for abstract classes with
230         default constructors, but on clang/gcc this will return false.  So we also need to check for that here.
231         @internal */
232     template<class Archive, class T> inline
233     typename std::enable_if<(traits::is_default_constructible<T>::value
234                              || traits::has_load_and_construct<T, Archive>::value)
235                              && !std::is_abstract<T>::value, bool>::type
serialize_wrapper(Archive & ar,std::shared_ptr<T> & ptr,std::uint32_t const nameid)236     serialize_wrapper(Archive & ar, std::shared_ptr<T> & ptr, std::uint32_t const nameid)
237     {
238       if(nameid & detail::msb2_32bit)
239       {
240         ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper(ptr)) );
241         return true;
242       }
243       return false;
244     }
245 
246     //! Serialize a unique_ptr if the 2nd msb in the nameid is set, and if we can actually construct the pointee
247     /*! This check lets us try and skip doing polymorphic machinery if we can get away with
248         using the derived class serialize function
249         @internal */
250     template<class Archive, class T, class D> inline
251     typename std::enable_if<(traits::is_default_constructible<T>::value
252                              || traits::has_load_and_construct<T, Archive>::value)
253                              && !std::is_abstract<T>::value, bool>::type
serialize_wrapper(Archive & ar,std::unique_ptr<T,D> & ptr,std::uint32_t const nameid)254     serialize_wrapper(Archive & ar, std::unique_ptr<T, D> & ptr, std::uint32_t const nameid)
255     {
256       if(nameid & detail::msb2_32bit)
257       {
258         ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper(ptr)) );
259         return true;
260       }
261       return false;
262     }
263 
264     //! Serialize a shared_ptr if the 2nd msb in the nameid is set, and if we can actually construct the pointee
265     /*! This case is for when we can't actually construct the shared pointer.  Normally this would be caught
266         as the pointer itself is serialized, but since this is a polymorphic pointer, if we tried to serialize
267         the pointer we'd end up back here recursively.  So we have to catch the error here as well, if
268         this was a polymorphic type serialized by its proper pointer type
269         @internal */
270     template<class Archive, class T> inline
271     typename std::enable_if<(!traits::is_default_constructible<T>::value
272                              && !traits::has_load_and_construct<T, Archive>::value)
273                              || std::is_abstract<T>::value, bool>::type
serialize_wrapper(Archive &,std::shared_ptr<T> &,std::uint32_t const nameid)274     serialize_wrapper(Archive &, std::shared_ptr<T> &, std::uint32_t const nameid)
275     {
276       if(nameid & detail::msb2_32bit)
277         throw cereal::Exception("Cannot load a polymorphic type that is not default constructable and does not have a load_and_construct function");
278       return false;
279     }
280 
281     //! Serialize a unique_ptr if the 2nd msb in the nameid is set, and if we can actually construct the pointee
282     /*! This case is for when we can't actually construct the unique pointer.  Normally this would be caught
283         as the pointer itself is serialized, but since this is a polymorphic pointer, if we tried to serialize
284         the pointer we'd end up back here recursively.  So we have to catch the error here as well, if
285         this was a polymorphic type serialized by its proper pointer type
286         @internal */
287     template<class Archive, class T, class D> inline
288      typename std::enable_if<(!traits::is_default_constructible<T>::value
289                                && !traits::has_load_and_construct<T, Archive>::value)
290                                || std::is_abstract<T>::value, bool>::type
serialize_wrapper(Archive &,std::unique_ptr<T,D> &,std::uint32_t const nameid)291     serialize_wrapper(Archive &, std::unique_ptr<T, D> &, std::uint32_t const nameid)
292     {
293       if(nameid & detail::msb2_32bit)
294         throw cereal::Exception("Cannot load a polymorphic type that is not default constructable and does not have a load_and_construct function");
295       return false;
296     }
297   } // polymorphic_detail
298 
299   // ######################################################################
300   // Pointer serialization for polymorphic types
301 
302   //! Saving std::shared_ptr for polymorphic types, abstract
303   template <class Archive, class T> inline
304   typename std::enable_if<std::is_polymorphic<T>::value && std::is_abstract<T>::value, void>::type
CEREAL_SAVE_FUNCTION_NAME(Archive & ar,std::shared_ptr<T> const & ptr)305   CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::shared_ptr<T> const & ptr )
306   {
307     if(!ptr)
308     {
309       // same behavior as nullptr in memory implementation
310       ar( CEREAL_NVP_("polymorphic_id", std::uint32_t(0)) );
311       return;
312     }
313 
314     std::type_info const & ptrinfo = typeid(*ptr.get());
315     static std::type_info const & tinfo = typeid(T);
316     // ptrinfo can never be equal to T info since we can't have an instance
317     // of an abstract object
318     //  this implies we need to do the lookup
319 
320     auto const & bindingMap = detail::StaticObject<detail::OutputBindingMap<Archive>>::getInstance().map;
321 
322     auto binding = bindingMap.find(std::type_index(ptrinfo));
323     if(binding == bindingMap.end())
324       UNREGISTERED_POLYMORPHIC_EXCEPTION(save, cereal::util::demangle(ptrinfo.name()))
325 
326     binding->second.shared_ptr(&ar, ptr.get(), tinfo);
327   }
328 
329   //! Saving std::shared_ptr for polymorphic types, not abstract
330   template <class Archive, class T> inline
331   typename std::enable_if<std::is_polymorphic<T>::value && !std::is_abstract<T>::value, void>::type
CEREAL_SAVE_FUNCTION_NAME(Archive & ar,std::shared_ptr<T> const & ptr)332   CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::shared_ptr<T> const & ptr )
333   {
334     if(!ptr)
335     {
336       // same behavior as nullptr in memory implementation
337       ar( CEREAL_NVP_("polymorphic_id", std::uint32_t(0)) );
338       return;
339     }
340 
341     std::type_info const & ptrinfo = typeid(*ptr.get());
342     static std::type_info const & tinfo = typeid(T);
343 
344     if(ptrinfo == tinfo)
345     {
346       // The 2nd msb signals that the following pointer does not need to be
347       // cast with our polymorphic machinery
348       ar( CEREAL_NVP_("polymorphic_id", detail::msb2_32bit) );
349 
350       ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper(ptr)) );
351 
352       return;
353     }
354 
355     auto const & bindingMap = detail::StaticObject<detail::OutputBindingMap<Archive>>::getInstance().map;
356 
357     auto binding = bindingMap.find(std::type_index(ptrinfo));
358     if(binding == bindingMap.end())
359       UNREGISTERED_POLYMORPHIC_EXCEPTION(save, cereal::util::demangle(ptrinfo.name()))
360 
361     binding->second.shared_ptr(&ar, ptr.get(), tinfo);
362   }
363 
364   //! Loading std::shared_ptr for polymorphic types
365   template <class Archive, class T> inline
366   typename std::enable_if<std::is_polymorphic<T>::value, void>::type
CEREAL_LOAD_FUNCTION_NAME(Archive & ar,std::shared_ptr<T> & ptr)367   CEREAL_LOAD_FUNCTION_NAME( Archive & ar, std::shared_ptr<T> & ptr )
368   {
369     std::uint32_t nameid;
370     ar( CEREAL_NVP_("polymorphic_id", nameid) );
371 
372     // Check to see if we can skip all of this polymorphism business
373     if(polymorphic_detail::serialize_wrapper(ar, ptr, nameid))
374       return;
375 
376     auto binding = polymorphic_detail::getInputBinding(ar, nameid);
377     std::shared_ptr<void> result;
378     binding.shared_ptr(&ar, result, typeid(T));
379     ptr = std::static_pointer_cast<T>(result);
380   }
381 
382   //! Saving std::weak_ptr for polymorphic types
383   template <class Archive, class T> inline
384   typename std::enable_if<std::is_polymorphic<T>::value, void>::type
CEREAL_SAVE_FUNCTION_NAME(Archive & ar,std::weak_ptr<T> const & ptr)385   CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::weak_ptr<T> const & ptr )
386   {
387     auto const sptr = ptr.lock();
388     ar( CEREAL_NVP_("locked_ptr", sptr) );
389   }
390 
391   //! Loading std::weak_ptr for polymorphic types
392   template <class Archive, class T> inline
393   typename std::enable_if<std::is_polymorphic<T>::value, void>::type
CEREAL_LOAD_FUNCTION_NAME(Archive & ar,std::weak_ptr<T> & ptr)394   CEREAL_LOAD_FUNCTION_NAME( Archive & ar, std::weak_ptr<T> & ptr )
395   {
396     std::shared_ptr<T> sptr;
397     ar( CEREAL_NVP_("locked_ptr", sptr) );
398     ptr = sptr;
399   }
400 
401   //! Saving std::unique_ptr for polymorphic types that are abstract
402   template <class Archive, class T, class D> inline
403   typename std::enable_if<std::is_polymorphic<T>::value && std::is_abstract<T>::value, void>::type
CEREAL_SAVE_FUNCTION_NAME(Archive & ar,std::unique_ptr<T,D> const & ptr)404   CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::unique_ptr<T, D> const & ptr )
405   {
406     if(!ptr)
407     {
408       // same behavior as nullptr in memory implementation
409       ar( CEREAL_NVP_("polymorphic_id", std::uint32_t(0)) );
410       return;
411     }
412 
413     std::type_info const & ptrinfo = typeid(*ptr.get());
414     static std::type_info const & tinfo = typeid(T);
415     // ptrinfo can never be equal to T info since we can't have an instance
416     // of an abstract object
417     //  this implies we need to do the lookup
418 
419     auto const & bindingMap = detail::StaticObject<detail::OutputBindingMap<Archive>>::getInstance().map;
420 
421     auto binding = bindingMap.find(std::type_index(ptrinfo));
422     if(binding == bindingMap.end())
423       UNREGISTERED_POLYMORPHIC_EXCEPTION(save, cereal::util::demangle(ptrinfo.name()))
424 
425     binding->second.unique_ptr(&ar, ptr.get(), tinfo);
426   }
427 
428   //! Saving std::unique_ptr for polymorphic types, not abstract
429   template <class Archive, class T, class D> inline
430   typename std::enable_if<std::is_polymorphic<T>::value && !std::is_abstract<T>::value, void>::type
CEREAL_SAVE_FUNCTION_NAME(Archive & ar,std::unique_ptr<T,D> const & ptr)431   CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::unique_ptr<T, D> const & ptr )
432   {
433     if(!ptr)
434     {
435       // same behavior as nullptr in memory implementation
436       ar( CEREAL_NVP_("polymorphic_id", std::uint32_t(0)) );
437       return;
438     }
439 
440     std::type_info const & ptrinfo = typeid(*ptr.get());
441     static std::type_info const & tinfo = typeid(T);
442 
443     if(ptrinfo == tinfo)
444     {
445       // The 2nd msb signals that the following pointer does not need to be
446       // cast with our polymorphic machinery
447       ar( CEREAL_NVP_("polymorphic_id", detail::msb2_32bit) );
448 
449       ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper(ptr)) );
450 
451       return;
452     }
453 
454     auto const & bindingMap = detail::StaticObject<detail::OutputBindingMap<Archive>>::getInstance().map;
455 
456     auto binding = bindingMap.find(std::type_index(ptrinfo));
457     if(binding == bindingMap.end())
458       UNREGISTERED_POLYMORPHIC_EXCEPTION(save, cereal::util::demangle(ptrinfo.name()))
459 
460     binding->second.unique_ptr(&ar, ptr.get(), tinfo);
461   }
462 
463   //! Loading std::unique_ptr, case when user provides load_and_construct for polymorphic types
464   template <class Archive, class T, class D> inline
465   typename std::enable_if<std::is_polymorphic<T>::value, void>::type
CEREAL_LOAD_FUNCTION_NAME(Archive & ar,std::unique_ptr<T,D> & ptr)466   CEREAL_LOAD_FUNCTION_NAME( Archive & ar, std::unique_ptr<T, D> & ptr )
467   {
468     std::uint32_t nameid;
469     ar( CEREAL_NVP_("polymorphic_id", nameid) );
470 
471     // Check to see if we can skip all of this polymorphism business
472     if(polymorphic_detail::serialize_wrapper(ar, ptr, nameid))
473       return;
474 
475     auto binding = polymorphic_detail::getInputBinding(ar, nameid);
476     std::unique_ptr<void, ::cereal::detail::EmptyDeleter<void>> result;
477     binding.unique_ptr(&ar, result, typeid(T));
478     ptr.reset(static_cast<T*>(result.release()));
479   }
480 
481   #undef UNREGISTERED_POLYMORPHIC_EXCEPTION
482 } // namespace cereal
483 #endif // CEREAL_TYPES_POLYMORPHIC_HPP_
484