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 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"
40 
41 #ifdef _MSC_VER
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 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  {
185 
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 
193 
194  template<class Archive> inline
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())
220  return binding->second;
221  }
222 
224 
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
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 
245 
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
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 
263 
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
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 
280 
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
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 
301  template <class Archive, class T> inline
302  typename std::enable_if<std::is_polymorphic<T>::value && std::is_abstract<T>::value, void>::type
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 
328  template <class Archive, class T> inline
329  typename std::enable_if<std::is_polymorphic<T>::value && !std::is_abstract<T>::value, void>::type
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 
363  template <class Archive, class T> inline
364  typename std::enable_if<std::is_polymorphic<T>::value, void>::type
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 
381  template <class Archive, class T> inline
382  typename std::enable_if<std::is_polymorphic<T>::value, void>::type
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 
390  template <class Archive, class T> inline
391  typename std::enable_if<std::is_polymorphic<T>::value, void>::type
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 
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
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 
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
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 
462  template <class Archive, class T, class D> inline
463  typename std::enable_if<std::is_polymorphic<T>::value, void>::type
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_
#define CEREAL_NVP_(name, value)
Convenience for creating a templated NVP.
Definition: helpers.hpp:199
#define UNREGISTERED_POLYMORPHIC_EXCEPTION(LoadSave, Name)
Error message used for unregistered polymorphic types.
Definition: polymorphic.hpp:186
Internal type trait support.
Internal helper functionality.
Definition: access.hpp:40
Internal misc utilities.
Main cereal functionality.
#define CEREAL_LOAD_FUNCTION_NAME
The deserialization (load) function name to search for.
Definition: macros.hpp:85
Internal polymorphism support.
#define CEREAL_SAVE_FUNCTION_NAME
The serialization (save) function name to search for.
Definition: macros.hpp:92
Support for types found in
An exception class thrown when things go wrong at runtime.
Definition: helpers.hpp:48