cereal_fwd
Fork of cereal library adding archive supporting forward compatibility
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", detail::make_polymorphic_key_tag(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  template <class Archive> inline
225  bool hasPolymorphicBinding(const std::string& name) {
226  auto const & bindingMap = detail::StaticObject<detail::InputBindingMap<Archive>>::getInstance().map;
227  return bindingMap.end() != bindingMap.find(name);
228  }
229 
231 
237  template<class Archive, class T> inline
238  typename std::enable_if<(traits::is_default_constructible<T>::value
240  && !std::is_abstract<T>::value, bool>::type
241  serialize_wrapper(Archive & ar, std::shared_ptr<T> & ptr, std::uint32_t const nameid)
242  {
243  if(nameid & detail::msb2_32bit)
244  {
245  ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper(ptr)) );
246  return true;
247  }
248  return false;
249  }
250 
252 
255  template<class Archive, class T, class D> inline
256  typename std::enable_if<(traits::is_default_constructible<T>::value
258  && !std::is_abstract<T>::value, bool>::type
259  serialize_wrapper(Archive & ar, std::unique_ptr<T, D> & ptr, std::uint32_t const nameid)
260  {
261  if(nameid & detail::msb2_32bit)
262  {
263  ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper(ptr)) );
264  return true;
265  }
266  return false;
267  }
268 
270 
275  template<class Archive, class T> inline
276  typename std::enable_if<(!traits::is_default_constructible<T>::value
278  || std::is_abstract<T>::value, bool>::type
279  serialize_wrapper(Archive &, std::shared_ptr<T> &, std::uint32_t const nameid)
280  {
281  if(nameid & detail::msb2_32bit)
282  throw cereal::Exception("Cannot load a polymorphic type that is not default constructable and does not have a load_and_construct function");
283  return false;
284  }
285 
287 
292  template<class Archive, class T, class D> inline
293  typename std::enable_if<(!traits::is_default_constructible<T>::value
295  || std::is_abstract<T>::value, bool>::type
296  serialize_wrapper(Archive &, std::unique_ptr<T, D> &, std::uint32_t const nameid)
297  {
298  if(nameid & detail::msb2_32bit)
299  throw cereal::Exception("Cannot load a polymorphic type that is not default constructable and does not have a load_and_construct function");
300  return false;
301  }
302  } // polymorphic_detail
303 
304  // ######################################################################
305  // Pointer serialization for polymorphic types
306 
308  template <class Archive, class T> inline
309  typename std::enable_if<std::is_polymorphic<T>::value && std::is_abstract<T>::value, void>::type
310  CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::shared_ptr<T> const & ptr )
311  {
312  if(!ptr)
313  {
314  // same behavior as nullptr in memory implementation
315  ar( CEREAL_NVP_("polymorphic_id", detail::make_polymorphic_id_tag(std::uint32_t(0))) );
316  return;
317  }
318 
319  std::type_info const & ptrinfo = typeid(*ptr.get());
320  static std::type_info const & tinfo = typeid(T);
321  // ptrinfo can never be equal to T info since we can't have an instance
322  // of an abstract object
323  // this implies we need to do the lookup
324 
325  auto const & bindingMap = detail::StaticObject<detail::OutputBindingMap<Archive>>::getInstance().map;
326 
327  auto binding = bindingMap.find(std::type_index(ptrinfo));
328  if(binding == bindingMap.end())
329  UNREGISTERED_POLYMORPHIC_EXCEPTION(save, cereal::util::demangle(ptrinfo.name()))
330 
331  binding->second.shared_ptr(&ar, ptr.get(), tinfo);
332  }
333 
335  template <class Archive, class T> inline
336  typename std::enable_if<std::is_polymorphic<T>::value && !std::is_abstract<T>::value, void>::type
337  CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::shared_ptr<T> const & ptr )
338  {
339  if(!ptr)
340  {
341  // same behavior as nullptr in memory implementation
342  ar( CEREAL_NVP_("polymorphic_id", detail::make_polymorphic_id_tag(std::uint32_t(0))) );
343  return;
344  }
345 
346  std::type_info const & ptrinfo = typeid(*ptr.get());
347  static std::type_info const & tinfo = typeid(T);
348 
349  if(ptrinfo == tinfo)
350  {
351  // The 2nd msb signals that the following pointer does not need to be
352  // cast with our polymorphic machinery
353  ar( CEREAL_NVP_("polymorphic_id", detail::make_polymorphic_id_tag(detail::msb2_32bit)) );
354 
355  ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper(ptr)) );
356 
357  return;
358  }
359 
360  auto const & bindingMap = detail::StaticObject<detail::OutputBindingMap<Archive>>::getInstance().map;
361 
362  auto binding = bindingMap.find(std::type_index(ptrinfo));
363  if(binding == bindingMap.end())
364  UNREGISTERED_POLYMORPHIC_EXCEPTION(save, cereal::util::demangle(ptrinfo.name()))
365 
366  binding->second.shared_ptr(&ar, ptr.get(), tinfo);
367  }
368 
370  template <class Archive, class T> inline
371  typename std::enable_if<std::is_polymorphic<T>::value, void>::type
372  CEREAL_LOAD_FUNCTION_NAME( Archive & ar, std::shared_ptr<T> & ptr )
373  {
374  std::uint32_t nameid;
375  ar( CEREAL_NVP_("polymorphic_id", detail::make_polymorphic_id_tag(nameid)) );
376 
377  // Check to see if we can skip all of this polymorphism business
378  if(polymorphic_detail::serialize_wrapper(ar, ptr, nameid))
379  return;
380 
381  auto binding = polymorphic_detail::getInputBinding(ar, nameid);
382  std::shared_ptr<void> result;
383  binding.shared_ptr(&ar, result, typeid(T));
384  ptr = std::static_pointer_cast<T>(result);
385  }
386 
388  template <class Archive, class T> inline
389  typename std::enable_if<std::is_polymorphic<T>::value, void>::type
390  CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::weak_ptr<T> const & ptr )
391  {
392  auto const sptr = ptr.lock();
393  ar( CEREAL_NVP_("locked_ptr", sptr) );
394  }
395 
397  template <class Archive, class T> inline
398  typename std::enable_if<std::is_polymorphic<T>::value, void>::type
399  CEREAL_LOAD_FUNCTION_NAME( Archive & ar, std::weak_ptr<T> & ptr )
400  {
401  std::shared_ptr<T> sptr;
402  ar( CEREAL_NVP_("locked_ptr", sptr) );
403  ptr = sptr;
404  }
405 
407  template <class Archive, class T, class D> inline
408  typename std::enable_if<std::is_polymorphic<T>::value && std::is_abstract<T>::value, void>::type
409  CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::unique_ptr<T, D> const & ptr )
410  {
411  if(!ptr)
412  {
413  // same behavior as nullptr in memory implementation
414  ar( CEREAL_NVP_("polymorphic_id", detail::make_polymorphic_id_tag(std::uint32_t(0))) );
415  return;
416  }
417 
418  std::type_info const & ptrinfo = typeid(*ptr.get());
419  static std::type_info const & tinfo = typeid(T);
420  // ptrinfo can never be equal to T info since we can't have an instance
421  // of an abstract object
422  // this implies we need to do the lookup
423 
424  auto const & bindingMap = detail::StaticObject<detail::OutputBindingMap<Archive>>::getInstance().map;
425 
426  auto binding = bindingMap.find(std::type_index(ptrinfo));
427  if(binding == bindingMap.end())
428  UNREGISTERED_POLYMORPHIC_EXCEPTION(save, cereal::util::demangle(ptrinfo.name()))
429 
430  binding->second.unique_ptr(&ar, ptr.get(), tinfo);
431  }
432 
434  template <class Archive, class T, class D> inline
435  typename std::enable_if<std::is_polymorphic<T>::value && !std::is_abstract<T>::value, void>::type
436  CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::unique_ptr<T, D> const & ptr )
437  {
438  if(!ptr)
439  {
440  // same behavior as nullptr in memory implementation
441  ar( CEREAL_NVP_("polymorphic_id", detail::make_polymorphic_id_tag(std::uint32_t(0))) );
442  return;
443  }
444 
445  std::type_info const & ptrinfo = typeid(*ptr.get());
446  static std::type_info const & tinfo = typeid(T);
447 
448  if(ptrinfo == tinfo)
449  {
450  // The 2nd msb signals that the following pointer does not need to be
451  // cast with our polymorphic machinery
452  ar( CEREAL_NVP_("polymorphic_id", detail::make_polymorphic_id_tag(detail::msb2_32bit)) );
453 
454  ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper(ptr)) );
455 
456  return;
457  }
458 
459  auto const & bindingMap = detail::StaticObject<detail::OutputBindingMap<Archive>>::getInstance().map;
460 
461  auto binding = bindingMap.find(std::type_index(ptrinfo));
462  if(binding == bindingMap.end())
463  UNREGISTERED_POLYMORPHIC_EXCEPTION(save, cereal::util::demangle(ptrinfo.name()))
464 
465  binding->second.unique_ptr(&ar, ptr.get(), tinfo);
466  }
467 
469  template <class Archive, class T, class D> inline
470  typename std::enable_if<std::is_polymorphic<T>::value, void>::type
471  CEREAL_LOAD_FUNCTION_NAME( Archive & ar, std::unique_ptr<T, D> & ptr )
472  {
473  std::uint32_t nameid;
474  ar( CEREAL_NVP_("polymorphic_id", detail::make_polymorphic_id_tag(nameid)) );
475 
476  // Check to see if we can skip all of this polymorphism business
477  if(polymorphic_detail::serialize_wrapper(ar, ptr, nameid))
478  return;
479 
480  auto binding = polymorphic_detail::getInputBinding(ar, nameid);
481  std::unique_ptr<void, ::cereal::detail::EmptyDeleter<void>> result;
482  binding.unique_ptr(&ar, result, typeid(T));
483  ptr.reset(static_cast<T*>(result.release()));
484  }
485 
486  #undef UNREGISTERED_POLYMORPHIC_EXCEPTION
487 } // namespace cereal
488 #endif // CEREAL_TYPES_POLYMORPHIC_HPP_
Non member load and construct check.
Definition: traits.hpp:917
#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:241
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:195
#define UNREGISTERED_POLYMORPHIC_EXCEPTION(LoadSave, Name)
Error message used for unregistered polymorphic types.
Definition: polymorphic.hpp:186
Internal type trait support.
Internal helper functionality.
bool hasPolymorphicBinding(const std::string &name)
Returns if given name was registered for polymorphic loading.
Definition: polymorphic.hpp:225
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:71
Internal polymorphism support.
#define CEREAL_SAVE_FUNCTION_NAME
The serialization (save) function name to search for.
Definition: macros.hpp:78
A static, pre-execution object.
Definition: static_object.hpp:67
Support for types found in <memory>
An exception class thrown when things go wrong at runtime.
Definition: helpers.hpp:48