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 STATIC_CONSTEXPR static
43 #else
44 #define STATIC_CONSTEXPR static constexpr
45 #endif
46 
48 
82 #define CEREAL_REGISTER_TYPE(T) \
83  namespace cereal { \
84  namespace detail { \
85  template <> \
86  struct binding_name<T> \
87  { \
88  STATIC_CONSTEXPR char const * name() { return #T; } \
89  }; \
90  } } /* end namespaces */ \
91  CEREAL_BIND_TO_ARCHIVES(T)
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  { STATIC_CONSTEXPR char const * name() { return Name; } }; \
105  } } /* end namespaces */ \
106  CEREAL_BIND_TO_ARCHIVES(T)
107 
110 
134 #define CEREAL_REGISTER_DYNAMIC_INIT(LibName) \
135  namespace cereal { \
136  namespace detail { \
137  void CEREAL_DLL_EXPORT dynamic_init_dummy_##LibName() {} \
138  } } /* end namespaces */
139 
142 
147 #define CEREAL_FORCE_DYNAMIC_INIT(LibName) \
148  namespace cereal { \
149  namespace detail { \
150  void dynamic_init_dummy_##LibName(); \
151  } /* end detail */ \
152  namespace { \
153  void dynamic_init_##LibName() \
154  { \
155  ::cereal::detail::dynamic_init_dummy_##LibName(); \
156  } \
157  } } /* end namespaces */
158 
159 #ifdef _MSC_VER
160 #undef CONSTEXPR
161 #endif
162 
163 namespace cereal
164 {
165  namespace polymorphic_detail
166  {
168 
169  #define UNREGISTERED_POLYMORPHIC_EXCEPTION(LoadSave, Name) \
170  throw cereal::Exception("Trying to " #LoadSave " an unregistered polymorphic type (" + Name + ").\n" \
171  "Make sure your type is registered with CEREAL_REGISTER_TYPE and that the archive " \
172  "you are using was included (and registered with CEREAL_REGISTER_ARCHIVE) prior to calling CEREAL_REGISTER_TYPE.\n" \
173  "If your type is already registered and you still see this error, you may need to use CEREAL_REGISTER_DYNAMIC_INIT.");
174 
176 
177  template<class Archive> inline
178  typename ::cereal::detail::InputBindingMap<Archive>::Serializers getInputBinding(Archive & ar, std::uint32_t const nameid)
179  {
180  // If the nameid is zero, we serialized a null pointer
181  if(nameid == 0)
182  {
183  typename ::cereal::detail::InputBindingMap<Archive>::Serializers emptySerializers;
184  emptySerializers.shared_ptr = [](void*, std::shared_ptr<void> & ptr) { ptr.reset(); };
185  emptySerializers.unique_ptr = [](void*, std::unique_ptr<void, ::cereal::detail::EmptyDeleter<void>> & ptr) { ptr.reset( nullptr ); };
186  return emptySerializers;
187  }
188 
189  std::string name;
190  if(nameid & detail::msb_32bit)
191  {
192  ar( CEREAL_NVP_("polymorphic_name", name) );
193  ar.registerPolymorphicName(nameid, name);
194  }
195  else
196  name = ar.getPolymorphicName(nameid);
197 
198  auto & bindingMap = detail::StaticObject<detail::InputBindingMap<Archive>>::getInstance().map;
199 
200  auto binding = bindingMap.find(name);
201  if(binding == bindingMap.end())
203  return binding->second;
204  }
205 
207 
213  template<class Archive, class T> inline
214  typename std::enable_if<(traits::is_default_constructible<T>::value
216  && !std::is_abstract<T>::value, bool>::type
217  serialize_wrapper(Archive & ar, std::shared_ptr<T> & ptr, std::uint32_t const nameid)
218  {
219  if(nameid & detail::msb2_32bit)
220  {
221  ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper(ptr)) );
222  return true;
223  }
224  return false;
225  }
226 
228 
231  template<class Archive, class T, class D> inline
232  typename std::enable_if<(traits::is_default_constructible<T>::value
234  && !std::is_abstract<T>::value, bool>::type
235  serialize_wrapper(Archive & ar, std::unique_ptr<T, D> & ptr, std::uint32_t const nameid)
236  {
237  if(nameid & detail::msb2_32bit)
238  {
239  ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper(ptr)) );
240  return true;
241  }
242  return false;
243  }
244 
246 
251  template<class Archive, class T> inline
252  typename std::enable_if<(!traits::is_default_constructible<T>::value
254  || std::is_abstract<T>::value, bool>::type
255  serialize_wrapper(Archive &, std::shared_ptr<T> &, std::uint32_t const nameid)
256  {
257  if(nameid & detail::msb2_32bit)
258  throw cereal::Exception("Cannot load a polymorphic type that is not default constructable and does not have a load_and_construct function");
259  return false;
260  }
261 
263 
268  template<class Archive, class T, class D> inline
269  typename std::enable_if<(!traits::is_default_constructible<T>::value
271  || std::is_abstract<T>::value, bool>::type
272  serialize_wrapper(Archive &, std::unique_ptr<T, D> &, 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  } // polymorphic_detail
279 
280  // ######################################################################
281  // Pointer serialization for polymorphic types
282 
284  template <class Archive, class T> inline
285  typename std::enable_if<std::is_polymorphic<T>::value && std::is_abstract<T>::value, void>::type
286  CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::shared_ptr<T> const & ptr )
287  {
288  if(!ptr)
289  {
290  // same behavior as nullptr in memory implementation
291  ar( CEREAL_NVP_("polymorphic_id", std::uint32_t(0)) );
292  return;
293  }
294 
295  std::type_info const & ptrinfo = typeid(*ptr.get());
296  // ptrinfo can never be equal to T info since we can't have an instance
297  // of an abstract object
298  // this implies we need to do the lookup
299 
300  auto & bindingMap = detail::StaticObject<detail::OutputBindingMap<Archive>>::getInstance().map;
301 
302  auto binding = bindingMap.find(std::type_index(ptrinfo));
303  if(binding == bindingMap.end())
304  UNREGISTERED_POLYMORPHIC_EXCEPTION(save, cereal::util::demangle(ptrinfo.name()))
305 
306  binding->second.shared_ptr(&ar, ptr.get());
307  }
308 
310  template <class Archive, class T> inline
311  typename std::enable_if<std::is_polymorphic<T>::value && !std::is_abstract<T>::value, void>::type
312  CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::shared_ptr<T> const & ptr )
313  {
314  if(!ptr)
315  {
316  // same behavior as nullptr in memory implementation
317  ar( CEREAL_NVP_("polymorphic_id", std::uint32_t(0)) );
318  return;
319  }
320 
321  std::type_info const & ptrinfo = typeid(*ptr.get());
322  static std::type_info const & tinfo = typeid(T);
323 
324  if(ptrinfo == tinfo)
325  {
326  // The 2nd msb signals that the following pointer does not need to be
327  // cast with our polymorphic machinery
328  ar( CEREAL_NVP_("polymorphic_id", detail::msb2_32bit) );
329 
330  ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper(ptr)) );
331 
332  return;
333  }
334 
335  auto & bindingMap = detail::StaticObject<detail::OutputBindingMap<Archive>>::getInstance().map;
336 
337  auto binding = bindingMap.find(std::type_index(ptrinfo));
338  if(binding == bindingMap.end())
339  UNREGISTERED_POLYMORPHIC_EXCEPTION(save, cereal::util::demangle(ptrinfo.name()))
340 
341  binding->second.shared_ptr(&ar, ptr.get());
342  }
343 
345  template <class Archive, class T> inline
346  typename std::enable_if<std::is_polymorphic<T>::value, void>::type
347  CEREAL_LOAD_FUNCTION_NAME( Archive & ar, std::shared_ptr<T> & ptr )
348  {
349  std::uint32_t nameid;
350  ar( CEREAL_NVP_("polymorphic_id", nameid) );
351 
352  // Check to see if we can skip all of this polymorphism business
353  if(polymorphic_detail::serialize_wrapper(ar, ptr, nameid))
354  return;
355 
356  auto binding = polymorphic_detail::getInputBinding(ar, nameid);
357  std::shared_ptr<void> result;
358  binding.shared_ptr(&ar, result);
359  ptr = std::static_pointer_cast<T>(result);
360  }
361 
363  template <class Archive, class T> inline
364  typename std::enable_if<std::is_polymorphic<T>::value, void>::type
365  CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::weak_ptr<T> const & ptr )
366  {
367  auto const sptr = ptr.lock();
368  ar( CEREAL_NVP_("locked_ptr", sptr) );
369  }
370 
372  template <class Archive, class T> inline
373  typename std::enable_if<std::is_polymorphic<T>::value, void>::type
374  CEREAL_LOAD_FUNCTION_NAME( Archive & ar, std::weak_ptr<T> & ptr )
375  {
376  std::shared_ptr<T> sptr;
377  ar( CEREAL_NVP_("locked_ptr", sptr) );
378  ptr = sptr;
379  }
380 
382  template <class Archive, class T, class D> inline
383  typename std::enable_if<std::is_polymorphic<T>::value && std::is_abstract<T>::value, void>::type
384  CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::unique_ptr<T, D> const & ptr )
385  {
386  if(!ptr)
387  {
388  // same behavior as nullptr in memory implementation
389  ar( CEREAL_NVP_("polymorphic_id", std::uint32_t(0)) );
390  return;
391  }
392 
393  std::type_info const & ptrinfo = typeid(*ptr.get());
394  // ptrinfo can never be equal to T info since we can't have an instance
395  // of an abstract object
396  // this implies we need to do the lookup
397 
398  auto & bindingMap = detail::StaticObject<detail::OutputBindingMap<Archive>>::getInstance().map;
399 
400  auto binding = bindingMap.find(std::type_index(ptrinfo));
401  if(binding == bindingMap.end())
402  UNREGISTERED_POLYMORPHIC_EXCEPTION(save, cereal::util::demangle(ptrinfo.name()))
403 
404  binding->second.unique_ptr(&ar, ptr.get());
405  }
406 
408  template <class Archive, class T, class D> inline
409  typename std::enable_if<std::is_polymorphic<T>::value && !std::is_abstract<T>::value, void>::type
410  CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::unique_ptr<T, D> const & ptr )
411  {
412  if(!ptr)
413  {
414  // same behavior as nullptr in memory implementation
415  ar( CEREAL_NVP_("polymorphic_id", std::uint32_t(0)) );
416  return;
417  }
418 
419  std::type_info const & ptrinfo = typeid(*ptr.get());
420  static std::type_info const & tinfo = typeid(T);
421 
422  if(ptrinfo == tinfo)
423  {
424  // The 2nd msb signals that the following pointer does not need to be
425  // cast with our polymorphic machinery
426  ar( CEREAL_NVP_("polymorphic_id", detail::msb2_32bit) );
427 
428  ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper(ptr)) );
429 
430  return;
431  }
432 
433  auto & bindingMap = detail::StaticObject<detail::OutputBindingMap<Archive>>::getInstance().map;
434 
435  auto binding = bindingMap.find(std::type_index(ptrinfo));
436  if(binding == bindingMap.end())
437  UNREGISTERED_POLYMORPHIC_EXCEPTION(save, cereal::util::demangle(ptrinfo.name()))
438 
439  binding->second.unique_ptr(&ar, ptr.get());
440  }
441 
443  template <class Archive, class T, class D> inline
444  typename std::enable_if<std::is_polymorphic<T>::value, void>::type
445  CEREAL_LOAD_FUNCTION_NAME( Archive & ar, std::unique_ptr<T, D> & ptr )
446  {
447  std::uint32_t nameid;
448  ar( CEREAL_NVP_("polymorphic_id", nameid) );
449 
450  // Check to see if we can skip all of this polymorphism business
451  if(polymorphic_detail::serialize_wrapper(ar, ptr, nameid))
452  return;
453 
454  auto binding = polymorphic_detail::getInputBinding(ar, nameid);
455  std::unique_ptr<void, ::cereal::detail::EmptyDeleter<void>> result;
456  binding.unique_ptr(&ar, result);
457  ptr.reset(static_cast<T*>(result.release()));
458  }
459 
460  #undef UNREGISTERED_POLYMORPHIC_EXCEPTION
461 } // namespace cereal
462 #endif // CEREAL_TYPES_POLYMORPHIC_HPP_
Has either a member or non member allocate.
Definition: traits.hpp:1192
#define CEREAL_NVP_(name, value)
Convenience for creating a templated NVP.
Definition: helpers.hpp:197
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:217
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:178
#define UNREGISTERED_POLYMORPHIC_EXCEPTION(LoadSave, Name)
Error message used for unregistered polymorphic types.
Definition: polymorphic.hpp:169
Internal type trait support.
Internal helper functionality.
Definition: access.hpp:39
Internal misc utilities.
Main cereal functionality.
#define CEREAL_LOAD_FUNCTION_NAME
The deserialization (load) function name to search for.
Definition: macros.hpp:58
Internal polymorphism support.
#define CEREAL_SAVE_FUNCTION_NAME
The serialization (save) function name to search for.
Definition: macros.hpp:65
A static, pre-execution object.
Definition: static_object.hpp:61
Support for types found in <memory>
An exception class thrown when things go wrong at runtime.
Definition: helpers.hpp:48