cereal
A C++11 library for serialization
polymorphic_impl.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 
31 /* This code is heavily inspired by the boost serialization implementation by the following authors
32 
33  (C) Copyright 2002 Robert Ramey - http://www.rrsd.com .
34  Use, modification and distribution is subject to the Boost Software
35  License, Version 1.0. (See http://www.boost.org/LICENSE_1_0.txt)
36 
37  See http://www.boost.org for updates, documentation, and revision history.
38 
39  (C) Copyright 2006 David Abrahams - http://www.boost.org.
40 
41  See /boost/serialization/export.hpp and /boost/archive/detail/register_archive.hpp for their
42  implementation.
43 */
44 #ifndef CEREAL_DETAILS_POLYMORPHIC_IMPL_HPP_
45 #define CEREAL_DETAILS_POLYMORPHIC_IMPL_HPP_
46 
48 #include <cereal/types/memory.hpp>
49 #include <cereal/types/string.hpp>
50 #include <functional>
51 #include <typeindex>
52 #include <map>
53 
55 
59 #define CEREAL_BIND_TO_ARCHIVES(T) \
60  namespace cereal { \
61  namespace detail { \
62  template<> \
63  struct init_binding<T> { \
64  static bind_to_archives<T> const & b; \
65  static void unused() { (void)b; } \
66  }; \
67  bind_to_archives<T> const & init_binding<T>::b = \
68  ::cereal::detail::StaticObject< \
69  bind_to_archives<T> \
70  >::getInstance().bind(); \
71  }} /* end namespaces */
72 
73 namespace cereal
74 {
75  namespace detail
76  {
78  template <class T>
79  struct binding_name {};
80 
82 
86  template <class Archive>
88  {
90 
94  typedef std::function<void(void*, void const *)> Serializer;
95 
97  struct Serializers
98  {
99  Serializer shared_ptr,
100  unique_ptr;
101  };
102 
104  std::map<std::type_index, Serializers> map;
105  };
106 
108  template<class T> struct EmptyDeleter { void operator()(T *) const {} };
109 
111 
115  template <class Archive>
117  {
119 
124  typedef std::function<void(void*, std::shared_ptr<void> & )> SharedSerializer;
126  typedef std::function<void(void*, std::unique_ptr<void, EmptyDeleter<void>> & )> UniqueSerializer;
127 
129  struct Serializers
130  {
133  };
134 
136  std::map<std::string, Serializers> map;
137  };
138 
139  // forward decls for archives from cereal.hpp
140  class InputArchiveBase;
141  class OutputArchiveBase;
142 
144 
148  template <class Archive, class T> struct InputBindingCreator
149  {
152  {
153  auto & map = StaticObject<InputBindingMap<Archive>>::getInstance().map;
154  auto key = std::string(binding_name<T>::name());
155  auto lb = map.lower_bound(key);
156 
157  if (lb != map.end() && lb->first == key)
158  return;
159 
160  typename InputBindingMap<Archive>::Serializers serializers;
161 
162  serializers.shared_ptr =
163  [](void * arptr, std::shared_ptr<void> & dptr)
164  {
165  Archive & ar = *static_cast<Archive*>(arptr);
166  std::shared_ptr<T> ptr;
167 
168  ar( CEREAL_NVP_("ptr_wrapper", ::cereal::memory_detail::make_ptr_wrapper(ptr)) );
169 
170  dptr = ptr;
171  };
172 
173  serializers.unique_ptr =
174  [](void * arptr, std::unique_ptr<void, EmptyDeleter<void>> & dptr)
175  {
176  Archive & ar = *static_cast<Archive*>(arptr);
177  std::unique_ptr<T> ptr;
178 
179  ar( CEREAL_NVP_("ptr_wrapper", ::cereal::memory_detail::make_ptr_wrapper(ptr)) );
180 
181  dptr.reset(ptr.release());
182  };
183 
184  map.insert( lb, { std::move(key), std::move(serializers) } );
185  }
186  };
187 
189 
193  template <class Archive, class T> struct OutputBindingCreator
194  {
196  static void writeMetadata(Archive & ar)
197  {
198  // Register the polymorphic type name with the archive, and get the id
199  char const * name = binding_name<T>::name();
200  std::uint32_t id = ar.registerPolymorphicType(name);
201 
202  // Serialize the id
203  ar( CEREAL_NVP_("polymorphic_id", id) );
204 
205  // If the msb of the id is 1, then the type name is new, and we should serialize it
206  if( id & detail::msb_32bit )
207  {
208  std::string namestring(name);
209  ar( CEREAL_NVP_("polymorphic_name", namestring) );
210  }
211  }
212 
215  {
216  public:
229  PolymorphicSharedPointerWrapper( void const * dptr ) : refCount()
230  {
231  #ifdef _LIBCPP_VERSION
232  // libc++ needs this hacky workaround, see http://llvm.org/bugs/show_bug.cgi?id=18843
233  wrappedPtr = std::shared_ptr<T const>(
234  std::const_pointer_cast<T const>(
235  std::shared_ptr<T>( refCount, static_cast<T *>(const_cast<void *>(dptr) ))));
236  #else // NOT _LIBCPP_VERSION
237  wrappedPtr = std::shared_ptr<T const>( refCount, static_cast<T const *>(dptr) );
238  #endif // _LIBCPP_VERSION
239  }
240 
242  inline std::shared_ptr<T const> const & operator()() const { return wrappedPtr; }
243 
244  private:
245  std::shared_ptr<void> refCount;
246  std::shared_ptr<T const> wrappedPtr;
247  };
248 
250 
257  static inline void savePolymorphicSharedPtr( Archive & ar, void const * dptr, std::true_type /* has_shared_from_this */ )
258  {
259  ::cereal::memory_detail::EnableSharedStateHelper<T> state( static_cast<T *>(const_cast<void *>(dptr)) );
260  PolymorphicSharedPointerWrapper psptr( dptr );
261  ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper( psptr() ) ) );
262  }
263 
265 
272  static inline void savePolymorphicSharedPtr( Archive & ar, void const * dptr, std::false_type /* has_shared_from_this */ )
273  {
274  PolymorphicSharedPointerWrapper psptr( dptr );
275  ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper( psptr() ) ) );
276  }
277 
280  {
281  auto & map = StaticObject<OutputBindingMap<Archive>>::getInstance().map;
282  auto key = std::type_index(typeid(T));
283  auto lb = map.lower_bound(key);
284 
285  if (lb != map.end() && lb->first == key)
286  return;
287 
288  typename OutputBindingMap<Archive>::Serializers serializers;
289 
290  serializers.shared_ptr =
291  [&](void * arptr, void const * dptr)
292  {
293  Archive & ar = *static_cast<Archive*>(arptr);
294 
295  writeMetadata(ar);
296 
297  #ifdef _MSC_VER
298  savePolymorphicSharedPtr( ar, dptr, ::cereal::traits::has_shared_from_this<T>::type() ); // MSVC doesn't like typename here
299  #else // not _MSC_VER
300  savePolymorphicSharedPtr( ar, dptr, typename ::cereal::traits::has_shared_from_this<T>::type() );
301  #endif // _MSC_VER
302  };
303 
304  serializers.unique_ptr =
305  [&](void * arptr, void const * dptr)
306  {
307  Archive & ar = *static_cast<Archive*>(arptr);
308 
309  writeMetadata(ar);
310 
311  std::unique_ptr<T const, EmptyDeleter<T const>> const ptr(static_cast<T const *>(dptr));
312 
313  ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper(ptr)) );
314  };
315 
316  map.insert( { std::move(key), std::move(serializers) } );
317  }
318  };
319 
322  struct adl_tag {};
323 
326  namespace { struct polymorphic_binding_tag {}; }
327 
329  template <class Archive, class T>
331  {
332  static const InputBindingCreator<Archive, T> &
333  load(std::true_type)
334  {
336  }
337 
338  static const OutputBindingCreator<Archive, T> &
339  save(std::true_type)
340  {
342  }
343 
344  inline static void load(std::false_type) {}
345  inline static void save(std::false_type) {}
346  };
347 
349  template <void(*)()>
351 
357  template <class Archive, class T>
359  {
360  #if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
361  virtual CEREAL_DLL_EXPORT void instantiate() CEREAL_USED;
364  #else // NOT _MSC_VER
365  static CEREAL_DLL_EXPORT void instantiate() CEREAL_USED;
370  #endif // _MSC_VER
371  };
372 
373  // instantiate implementation
374  template <class Archive, class T>
376  {
377  create_bindings<Archive,T>::save( std::integral_constant<bool,
378  std::is_base_of<detail::OutputArchiveBase, Archive>::value &&
380 
381  create_bindings<Archive,T>::load( std::integral_constant<bool,
382  std::is_base_of<detail::InputArchiveBase, Archive>::value &&
384  }
385 
387 
391  template <class T, class Tag = polymorphic_binding_tag>
393  {
395  void bind(std::false_type) const
396  {
397  instantiate_polymorphic_binding((T*) 0, 0, Tag{}, adl_tag{});
398  }
399 
401  void bind(std::true_type) const
402  { }
403 
405 
407  bind_to_archives const & bind() const
408  {
409  static_assert( std::is_polymorphic<T>::value,
410  "Attempting to register non polymorphic type" );
411  bind( std::is_abstract<T>() );
412  return *this;
413  }
414  };
415 
417  template <class T, class Tag = polymorphic_binding_tag>
418  struct init_binding;
419 
421 
432  template <class T, typename BindingTag>
433  void instantiate_polymorphic_binding( T*, int, BindingTag, adl_tag ) {}
434  } // namespace detail
435 } // namespace cereal
436 
437 #endif // CEREAL_DETAILS_POLYMORPHIC_IMPL_HPP_
#define CEREAL_NVP_(name, value)
Convenience for creating a templated NVP.
Definition: helpers.hpp:197
void instantiate_polymorphic_binding(T *, int, BindingTag, adl_tag)
Base case overload for instantiation.
Definition: polymorphic_impl.hpp:433
static void savePolymorphicSharedPtr(Archive &ar, void const *dptr, std::true_type)
Does the actual work of saving a polymorphic shared_ptr.
Definition: polymorphic_impl.hpp:257
Creates a binding (map entry) between an input archive type and a polymorphic type.
Definition: polymorphic_impl.hpp:148
Struct containing the serializer functions for all pointer types.
Definition: polymorphic_impl.hpp:97
When specialized, causes the compiler to instantiate its parameter.
Definition: polymorphic_impl.hpp:350
Used to hide the static object used to bind T to registered archives.
Definition: polymorphic_impl.hpp:418
Serializer unique_ptr
Serializer function for unique pointers.
Definition: polymorphic_impl.hpp:99
Holds a properly typed shared_ptr to the polymorphic type.
Definition: polymorphic_impl.hpp:214
std::shared_ptr< T const > const & operator()() const
Get the wrapped shared_ptr */.
Definition: polymorphic_impl.hpp:242
bind_to_archives const & bind() const
Binds the type T to all registered archives.
Definition: polymorphic_impl.hpp:407
Determine if T or any base class of T has inherited from std::enable_shared_from_this.
Definition: traits.hpp:1140
void bind(std::false_type) const
Binding for non abstract types.
Definition: polymorphic_impl.hpp:395
Definition: access.hpp:39
Serializer shared_ptr
Serializer function for shared/weak pointers.
Definition: polymorphic_impl.hpp:99
SharedSerializer shared_ptr
Serializer function for shared/weak pointers.
Definition: polymorphic_impl.hpp:131
Struct containing the serializer functions for all pointer types.
Definition: polymorphic_impl.hpp:129
std::function< void(void *, std::unique_ptr< void, EmptyDeleter< void >> &)> UniqueSerializer
Unique ptr serializer function.
Definition: polymorphic_impl.hpp:126
OutputBindingCreator()
Initialize the binding.
Definition: polymorphic_impl.hpp:279
std::map< std::string, Serializers > map
A map of serializers for pointers of all registered types.
Definition: polymorphic_impl.hpp:136
static CEREAL_DLL_EXPORT void instantiate() CEREAL_USED
Definition: polymorphic_impl.hpp:375
Definition: traits.hpp:1035
Definition: polymorphic_impl.hpp:322
Creates a binding (map entry) between an output archive type and a polymorphic type.
Definition: polymorphic_impl.hpp:193
Support for types found in <string>
Causes the static object bindings between an archive type and a serializable type T...
Definition: polymorphic_impl.hpp:330
static void writeMetadata(Archive &ar)
Writes appropriate metadata to the archive for this polymorphic type.
Definition: polymorphic_impl.hpp:196
An empty noop deleter.
Definition: polymorphic_impl.hpp:108
InputBindingCreator()
Initialize the binding.
Definition: polymorphic_impl.hpp:151
std::map< std::type_index, Serializers > map
A map of serializers for pointers of all registered types.
Definition: polymorphic_impl.hpp:104
instantiate_function< instantiate > unused
This typedef causes the compiler to instantiate this static function.
Definition: polymorphic_impl.hpp:369
Binds a compile time type with a user defined string.
Definition: polymorphic_impl.hpp:79
PolymorphicSharedPointerWrapper(void const *dptr)
Definition: polymorphic_impl.hpp:229
Definition: helpers.hpp:228
std::function< void(void *, void const *)> Serializer
A serializer function.
Definition: polymorphic_impl.hpp:94
#define CEREAL_DLL_EXPORT
Prevent link optimization from removing non-referenced static objects.
Definition: static_object.hpp:45
A structure holding a map from type name strings to input serializer functions.
Definition: polymorphic_impl.hpp:116
Begins the binding process of a type to all registered archives.
Definition: polymorphic_impl.hpp:392
Definition: helpers.hpp:229
std::function< void(void *, std::shared_ptr< void > &)> SharedSerializer
Shared ptr serializer function.
Definition: polymorphic_impl.hpp:124
Definition: traits.hpp:1062
UniqueSerializer unique_ptr
Serializer function for unique pointers.
Definition: polymorphic_impl.hpp:132
A static, pre-execution object.
Definition: static_object.hpp:61
static void savePolymorphicSharedPtr(Archive &ar, void const *dptr, std::false_type)
Does the actual work of saving a polymorphic shared_ptr.
Definition: polymorphic_impl.hpp:272
A structure holding a map from type_indices to output serializer functions.
Definition: polymorphic_impl.hpp:87
Support for types found in <memory>
Internal polymorphism static object support.
void bind(std::true_type) const
Binding for abstract types.
Definition: polymorphic_impl.hpp:401