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 dynamic_init_dummy_##LibName();                \
172   } /* end detail */                                    \
173   namespace {                                           \
174     void dynamic_init_##LibName()                       \
175     {                                                   \
176       ::cereal::detail::dynamic_init_dummy_##LibName(); \
177     }                                                   \
178   } } /* end namespaces */
179 
180 namespace cereal
181 {
182   namespace polymorphic_detail
183   {
184     //! Error message used for unregistered polymorphic types
185     /*! @internal */
186     #define UNREGISTERED_POLYMORPHIC_EXCEPTION(LoadSave, Name)                                                                                      \
187       throw cereal::Exception("Trying to " #LoadSave " an unregistered polymorphic type (" + Name + ").\n"                                          \
188                               "Make sure your type is registered with CEREAL_REGISTER_TYPE and that the archive "                                   \
189                               "you are using was included (and registered with CEREAL_REGISTER_ARCHIVE) prior to calling CEREAL_REGISTER_TYPE.\n"   \
190                               "If your type is already registered and you still see this error, you may need to use CEREAL_REGISTER_DYNAMIC_INIT.");
191 
192     //! Get an input binding from the given archive by deserializing the type meta data
193     /*! @internal */
194     template<class Archive> inline
getInputBinding(Archive & ar,std::uint32_t const nameid)195     typename ::cereal::detail::InputBindingMap<Archive>::Serializers getInputBinding(Archive & ar, std::uint32_t const nameid)
196     {
197       // If the nameid is zero, we serialized a null pointer
198       if(nameid == 0)
199       {
200         typename ::cereal::detail::InputBindingMap<Archive>::Serializers emptySerializers;
201         emptySerializers.shared_ptr = [](void*, std::shared_ptr<void> & ptr, std::type_info const &) { ptr.reset(); };
202         emptySerializers.unique_ptr = [](void*, std::unique_ptr<void, ::cereal::detail::EmptyDeleter<void>> & ptr, std::type_info const &) { ptr.reset( nullptr ); };
203         return emptySerializers;
204       }
205 
206       std::string name;
207       if(nameid & detail::msb_32bit)
208       {
209         ar( CEREAL_NVP_("polymorphic_name", name) );
210         ar.registerPolymorphicName(nameid, name);
211       }
212       else
213         name = ar.getPolymorphicName(nameid);
214 
215       auto const & bindingMap = detail::StaticObject<detail::InputBindingMap<Archive>>::getInstance().map;
216 
217       auto binding = bindingMap.find(name);
218       if(binding == bindingMap.end())
219         UNREGISTERED_POLYMORPHIC_EXCEPTION(load, name)
220       return binding->second;
221     }
222 
223     //! Serialize a shared_ptr if the 2nd msb in the nameid is set, and if we can actually construct the pointee
224     /*! This check lets us try and skip doing polymorphic machinery if we can get away with
225         using the derived class serialize function
226 
227         Note that on MSVC 2013 preview, is_default_constructible<T> returns true for abstract classes with
228         default constructors, but on clang/gcc this will return false.  So we also need to check for that here.
229         @internal */
230     template<class Archive, class T> inline
231     typename std::enable_if<(traits::is_default_constructible<T>::value
232                              || traits::has_load_and_construct<T, Archive>::value)
233                              && !std::is_abstract<T>::value, bool>::type
serialize_wrapper(Archive & ar,std::shared_ptr<T> & ptr,std::uint32_t const nameid)234     serialize_wrapper(Archive & ar, std::shared_ptr<T> & ptr, std::uint32_t const nameid)
235     {
236       if(nameid & detail::msb2_32bit)
237       {
238         ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper(ptr)) );
239         return true;
240       }
241       return false;
242     }
243 
244     //! Serialize a unique_ptr if the 2nd msb in the nameid is set, and if we can actually construct the pointee
245     /*! This check lets us try and skip doing polymorphic machinery if we can get away with
246         using the derived class serialize function
247         @internal */
248     template<class Archive, class T, class D> inline
249     typename std::enable_if<(traits::is_default_constructible<T>::value
250                              || traits::has_load_and_construct<T, Archive>::value)
251                              && !std::is_abstract<T>::value, bool>::type
serialize_wrapper(Archive & ar,std::unique_ptr<T,D> & ptr,std::uint32_t const nameid)252     serialize_wrapper(Archive & ar, std::unique_ptr<T, D> & ptr, std::uint32_t const nameid)
253     {
254       if(nameid & detail::msb2_32bit)
255       {
256         ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper(ptr)) );
257         return true;
258       }
259       return false;
260     }
261 
262     //! Serialize a shared_ptr if the 2nd msb in the nameid is set, and if we can actually construct the pointee
263     /*! This case is for when we can't actually construct the shared pointer.  Normally this would be caught
264         as the pointer itself is serialized, but since this is a polymorphic pointer, if we tried to serialize
265         the pointer we'd end up back here recursively.  So we have to catch the error here as well, if
266         this was a polymorphic type serialized by its proper pointer type
267         @internal */
268     template<class Archive, class T> inline
269     typename std::enable_if<(!traits::is_default_constructible<T>::value
270                              && !traits::has_load_and_construct<T, Archive>::value)
271                              || std::is_abstract<T>::value, bool>::type
serialize_wrapper(Archive &,std::shared_ptr<T> &,std::uint32_t const nameid)272     serialize_wrapper(Archive &, std::shared_ptr<T> &, std::uint32_t const nameid)
273     {
274       if(nameid & detail::msb2_32bit)
275         throw cereal::Exception("Cannot load a polymorphic type that is not default constructable and does not have a load_and_construct function");
276       return false;
277     }
278 
279     //! Serialize a unique_ptr if the 2nd msb in the nameid is set, and if we can actually construct the pointee
280     /*! This case is for when we can't actually construct the unique pointer.  Normally this would be caught
281         as the pointer itself is serialized, but since this is a polymorphic pointer, if we tried to serialize
282         the pointer we'd end up back here recursively.  So we have to catch the error here as well, if
283         this was a polymorphic type serialized by its proper pointer type
284         @internal */
285     template<class Archive, class T, class D> inline
286      typename std::enable_if<(!traits::is_default_constructible<T>::value
287                                && !traits::has_load_and_construct<T, Archive>::value)
288                                || std::is_abstract<T>::value, bool>::type
serialize_wrapper(Archive &,std::unique_ptr<T,D> &,std::uint32_t const nameid)289     serialize_wrapper(Archive &, std::unique_ptr<T, D> &, std::uint32_t const nameid)
290     {
291       if(nameid & detail::msb2_32bit)
292         throw cereal::Exception("Cannot load a polymorphic type that is not default constructable and does not have a load_and_construct function");
293       return false;
294     }
295   } // polymorphic_detail
296 
297   // ######################################################################
298   // Pointer serialization for polymorphic types
299 
300   //! Saving std::shared_ptr for polymorphic types, abstract
301   template <class Archive, class T> inline
302   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)303   CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::shared_ptr<T> const & ptr )
304   {
305     if(!ptr)
306     {
307       // same behavior as nullptr in memory implementation
308       ar( CEREAL_NVP_("polymorphic_id", std::uint32_t(0)) );
309       return;
310     }
311 
312     std::type_info const & ptrinfo = typeid(*ptr.get());
313     static std::type_info const & tinfo = typeid(T);
314     // ptrinfo can never be equal to T info since we can't have an instance
315     // of an abstract object
316     //  this implies we need to do the lookup
317 
318     auto const & bindingMap = detail::StaticObject<detail::OutputBindingMap<Archive>>::getInstance().map;
319 
320     auto binding = bindingMap.find(std::type_index(ptrinfo));
321     if(binding == bindingMap.end())
322       UNREGISTERED_POLYMORPHIC_EXCEPTION(save, cereal::util::demangle(ptrinfo.name()))
323 
324     binding->second.shared_ptr(&ar, ptr.get(), tinfo);
325   }
326 
327   //! Saving std::shared_ptr for polymorphic types, not abstract
328   template <class Archive, class T> inline
329   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)330   CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::shared_ptr<T> const & ptr )
331   {
332     if(!ptr)
333     {
334       // same behavior as nullptr in memory implementation
335       ar( CEREAL_NVP_("polymorphic_id", std::uint32_t(0)) );
336       return;
337     }
338 
339     std::type_info const & ptrinfo = typeid(*ptr.get());
340     static std::type_info const & tinfo = typeid(T);
341 
342     if(ptrinfo == tinfo)
343     {
344       // The 2nd msb signals that the following pointer does not need to be
345       // cast with our polymorphic machinery
346       ar( CEREAL_NVP_("polymorphic_id", detail::msb2_32bit) );
347 
348       ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper(ptr)) );
349 
350       return;
351     }
352 
353     auto const & bindingMap = detail::StaticObject<detail::OutputBindingMap<Archive>>::getInstance().map;
354 
355     auto binding = bindingMap.find(std::type_index(ptrinfo));
356     if(binding == bindingMap.end())
357       UNREGISTERED_POLYMORPHIC_EXCEPTION(save, cereal::util::demangle(ptrinfo.name()))
358 
359     binding->second.shared_ptr(&ar, ptr.get(), tinfo);
360   }
361 
362   //! Loading std::shared_ptr for polymorphic types
363   template <class Archive, class T> inline
364   typename std::enable_if<std::is_polymorphic<T>::value, void>::type
CEREAL_LOAD_FUNCTION_NAME(Archive & ar,std::shared_ptr<T> & ptr)365   CEREAL_LOAD_FUNCTION_NAME( Archive & ar, std::shared_ptr<T> & ptr )
366   {
367     std::uint32_t nameid;
368     ar( CEREAL_NVP_("polymorphic_id", nameid) );
369 
370     // Check to see if we can skip all of this polymorphism business
371     if(polymorphic_detail::serialize_wrapper(ar, ptr, nameid))
372       return;
373 
374     auto binding = polymorphic_detail::getInputBinding(ar, nameid);
375     std::shared_ptr<void> result;
376     binding.shared_ptr(&ar, result, typeid(T));
377     ptr = std::static_pointer_cast<T>(result);
378   }
379 
380   //! Saving std::weak_ptr for polymorphic types
381   template <class Archive, class T> inline
382   typename std::enable_if<std::is_polymorphic<T>::value, void>::type
CEREAL_SAVE_FUNCTION_NAME(Archive & ar,std::weak_ptr<T> const & ptr)383   CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::weak_ptr<T> const & ptr )
384   {
385     auto const sptr = ptr.lock();
386     ar( CEREAL_NVP_("locked_ptr", sptr) );
387   }
388 
389   //! Loading std::weak_ptr for polymorphic types
390   template <class Archive, class T> inline
391   typename std::enable_if<std::is_polymorphic<T>::value, void>::type
CEREAL_LOAD_FUNCTION_NAME(Archive & ar,std::weak_ptr<T> & ptr)392   CEREAL_LOAD_FUNCTION_NAME( Archive & ar, std::weak_ptr<T> & ptr )
393   {
394     std::shared_ptr<T> sptr;
395     ar( CEREAL_NVP_("locked_ptr", sptr) );
396     ptr = sptr;
397   }
398 
399   //! Saving std::unique_ptr for polymorphic types that are abstract
400   template <class Archive, class T, class D> inline
401   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)402   CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::unique_ptr<T, D> const & ptr )
403   {
404     if(!ptr)
405     {
406       // same behavior as nullptr in memory implementation
407       ar( CEREAL_NVP_("polymorphic_id", std::uint32_t(0)) );
408       return;
409     }
410 
411     std::type_info const & ptrinfo = typeid(*ptr.get());
412     static std::type_info const & tinfo = typeid(T);
413     // ptrinfo can never be equal to T info since we can't have an instance
414     // of an abstract object
415     //  this implies we need to do the lookup
416 
417     auto const & bindingMap = detail::StaticObject<detail::OutputBindingMap<Archive>>::getInstance().map;
418 
419     auto binding = bindingMap.find(std::type_index(ptrinfo));
420     if(binding == bindingMap.end())
421       UNREGISTERED_POLYMORPHIC_EXCEPTION(save, cereal::util::demangle(ptrinfo.name()))
422 
423     binding->second.unique_ptr(&ar, ptr.get(), tinfo);
424   }
425 
426   //! Saving std::unique_ptr for polymorphic types, not abstract
427   template <class Archive, class T, class D> inline
428   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)429   CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::unique_ptr<T, D> const & ptr )
430   {
431     if(!ptr)
432     {
433       // same behavior as nullptr in memory implementation
434       ar( CEREAL_NVP_("polymorphic_id", std::uint32_t(0)) );
435       return;
436     }
437 
438     std::type_info const & ptrinfo = typeid(*ptr.get());
439     static std::type_info const & tinfo = typeid(T);
440 
441     if(ptrinfo == tinfo)
442     {
443       // The 2nd msb signals that the following pointer does not need to be
444       // cast with our polymorphic machinery
445       ar( CEREAL_NVP_("polymorphic_id", detail::msb2_32bit) );
446 
447       ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper(ptr)) );
448 
449       return;
450     }
451 
452     auto const & bindingMap = detail::StaticObject<detail::OutputBindingMap<Archive>>::getInstance().map;
453 
454     auto binding = bindingMap.find(std::type_index(ptrinfo));
455     if(binding == bindingMap.end())
456       UNREGISTERED_POLYMORPHIC_EXCEPTION(save, cereal::util::demangle(ptrinfo.name()))
457 
458     binding->second.unique_ptr(&ar, ptr.get(), tinfo);
459   }
460 
461   //! Loading std::unique_ptr, case when user provides load_and_construct for polymorphic types
462   template <class Archive, class T, class D> inline
463   typename std::enable_if<std::is_polymorphic<T>::value, void>::type
CEREAL_LOAD_FUNCTION_NAME(Archive & ar,std::unique_ptr<T,D> & ptr)464   CEREAL_LOAD_FUNCTION_NAME( Archive & ar, std::unique_ptr<T, D> & ptr )
465   {
466     std::uint32_t nameid;
467     ar( CEREAL_NVP_("polymorphic_id", nameid) );
468 
469     // Check to see if we can skip all of this polymorphism business
470     if(polymorphic_detail::serialize_wrapper(ar, ptr, nameid))
471       return;
472 
473     auto binding = polymorphic_detail::getInputBinding(ar, nameid);
474     std::unique_ptr<void, ::cereal::detail::EmptyDeleter<void>> result;
475     binding.unique_ptr(&ar, result, typeid(T));
476     ptr.reset(static_cast<T*>(result.release()));
477   }
478 
479   #undef UNREGISTERED_POLYMORPHIC_EXCEPTION
480 } // namespace cereal
481 #endif // CEREAL_TYPES_POLYMORPHIC_HPP_
482