cereal
A C++11 library for serialization
polymorphic.hpp
Go to the documentation of this file.
1 
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 #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"
40 
41 #if defined(_MSC_VER) && _MSC_VER < 1916
42 #define CEREAL_STATIC_CONSTEXPR static
43 #else
44 #define CEREAL_STATIC_CONSTEXPR static constexpr
45 #endif
46 
48 
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 
95 
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 
109 
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 
131 
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 
163 
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  {
187 
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 
195 
196  template<class Archive> inline
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())
222  return binding->second;
223  }
224 
226 
232  template<class Archive, class T> inline
233  typename std::enable_if<(traits::is_default_constructible<T>::value
235  && !std::is_abstract<T>::value, bool>::type
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 
247 
250  template<class Archive, class T, class D> inline
251  typename std::enable_if<(traits::is_default_constructible<T>::value
253  && !std::is_abstract<T>::value, bool>::type
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 
265 
270  template<class Archive, class T> inline
271  typename std::enable_if<(!traits::is_default_constructible<T>::value
273  || std::is_abstract<T>::value, bool>::type
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 
282 
287  template<class Archive, class T, class D> inline
288  typename std::enable_if<(!traits::is_default_constructible<T>::value
290  || std::is_abstract<T>::value, bool>::type
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 
303  template <class Archive, class T> inline
304  typename std::enable_if<std::is_polymorphic<T>::value && std::is_abstract<T>::value, void>::type
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 
330  template <class Archive, class T> inline
331  typename std::enable_if<std::is_polymorphic<T>::value && !std::is_abstract<T>::value, void>::type
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 
365  template <class Archive, class T> inline
366  typename std::enable_if<std::is_polymorphic<T>::value, void>::type
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 
383  template <class Archive, class T> inline
384  typename std::enable_if<std::is_polymorphic<T>::value, void>::type
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 
392  template <class Archive, class T> inline
393  typename std::enable_if<std::is_polymorphic<T>::value, void>::type
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 
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
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 
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
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 
464  template <class Archive, class T, class D> inline
465  typename std::enable_if<std::is_polymorphic<T>::value, void>::type
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_
CEREAL_NVP_
#define CEREAL_NVP_(name, value)
Convenience for creating a templated NVP.
Definition: helpers.hpp:201
UNREGISTERED_POLYMORPHIC_EXCEPTION
#define UNREGISTERED_POLYMORPHIC_EXCEPTION(LoadSave, Name)
Error message used for unregistered polymorphic types.
Definition: polymorphic.hpp:188
cereal::polymorphic_detail::getInputBinding
typename ::cereal::detail::InputBindingMap< Archive >::Serializers getInputBinding(Archive &ar, std::uint32_t const nameid)
Get an input binding from the given archive by deserializing the type meta data.
Definition: polymorphic.hpp:197
memory.hpp
Support for types found in <memory>
helpers.hpp
Internal helper functionality.
CEREAL_LOAD_FUNCTION_NAME
#define CEREAL_LOAD_FUNCTION_NAME
The deserialization (load) function name to search for.
Definition: macros.hpp:85
cereal::detail::StaticObject
A static, pre-execution object.
Definition: static_object.hpp:67
cereal::traits::is_default_constructible
Determines whether the class T can be default constructed by cereal::access.
Definition: traits.hpp:1254
CEREAL_SAVE_FUNCTION_NAME
#define CEREAL_SAVE_FUNCTION_NAME
The serialization (save) function name to search for.
Definition: macros.hpp:92
polymorphic_impl.hpp
Internal polymorphism support.
cereal.hpp
Main cereal functionality.
cereal::Exception
An exception class thrown when things go wrong at runtime.
Definition: helpers.hpp:48
traits.hpp
Internal type trait support.
cereal::traits::has_load_and_construct
Non member load and construct check.
Definition: traits.hpp:939
util.hpp
Internal misc utilities.
cereal::polymorphic_detail::serialize_wrapper
std::enable_if<(traits::is_default_constructible< T >::value||traits::has_load_and_construct< T, Archive >::value) &&!std::is_abstract< T >::value, bool >::type serialize_wrapper(Archive &ar, std::shared_ptr< T > &ptr, std::uint32_t const nameid)
Serialize a shared_ptr if the 2nd msb in the nameid is set, and if we can actually construct the poin...
Definition: polymorphic.hpp:236