cereal
A C++11 library for serialization
 All Classes Files Functions Variables Typedefs Enumerations Groups
polymorphic.hpp
1 /*
2  Copyright (c) 2013, Randolph Voorhies, Shane Grant
3  All rights reserved.
4 
5  Redistribution and use in source and binary forms, with or without
6  modification, are permitted provided that the following conditions are met:
7  * Redistributions of source code must retain the above copyright
8  notice, this list of conditions and the following disclaimer.
9  * Redistributions in binary form must reproduce the above copyright
10  notice, this list of conditions and the following disclaimer in the
11  documentation and/or other materials provided with the distribution.
12  * Neither the name of cereal nor the
13  names of its contributors may be used to endorse or promote products
14  derived from this software without specific prior written permission.
15 
16  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
20  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27 #ifndef CEREAL_TYPES_POLYMORPHIC_HPP_
28 #define CEREAL_TYPES_POLYMORPHIC_HPP_
29 
30 #include <cereal/cereal.hpp>
31 #include <cereal/types/memory.hpp>
32 
33 #include <cereal/details/util.hpp>
34 #include <cereal/details/helpers.hpp>
35 #include <cereal/details/traits.hpp>
36 #include <cereal/details/polymorphic_impl.hpp>
37 
39 
53 #define CEREAL_REGISTER_TYPE(T) \
54  namespace cereal { \
55  namespace detail { \
56  template <> \
57  struct binding_name<T> \
58  { \
59  static constexpr char const * name() { return #T; }; \
60  }; \
61  } } /* end namespaces */ \
62  CEREAL_BIND_TO_ARCHIVES(T);
63 
66 
70 #define CEREAL_REGISTER_TYPE_WITH_NAME(T, Name) \
71  namespace cereal { \
72  namespace detail { \
73  template <> \
74  struct binding_name<T> \
75  { static constexpr char const * name() { return Name; }; }; \
76  } } /* end namespaces */ \
77  CEREAL_BIND_TO_ARCHIVES(T);
78 
79 namespace cereal
80 {
81  namespace polymorphic_detail
82  {
84  template<class Archive> inline
85  typename ::cereal::detail::InputBindingMap<Archive>::Serializers getInputBinding(Archive & ar, std::uint32_t const nameid)
86  {
87  if(nameid == 0)
88  {
89  typename ::cereal::detail::InputBindingMap<Archive>::Serializers emptySerializers;
90  emptySerializers.shared_ptr = [](void*, std::shared_ptr<void> & ptr) { ptr.reset(); };
91  emptySerializers.unique_ptr = [](void*, std::unique_ptr<void> & ptr) { ptr.reset(); };
92  return emptySerializers;
93  }
94 
95  std::string name;
96  if(nameid & detail::msb_32bit)
97  {
98  ar( name );
99  ar.registerPolymorphicName(nameid, name);
100  }
101  else
102  name = ar.getPolymorphicName(nameid);
103 
104  auto & bindingMap = detail::StaticObject<detail::InputBindingMap<Archive>>::getInstance().map;
105 
106  auto binding = bindingMap.find(name);
107  if(binding == bindingMap.end())
108  throw cereal::Exception("Trying to load an unregistered polymorphic type (" + name + ")");
109  return binding->second;
110  }
111 
113  template<class Archive, class T> inline
114  typename std::enable_if<std::is_default_constructible<T>::value || traits::has_load_and_allocate<T, Archive>(), bool>::type
115  serialize_wrapper(Archive & ar, std::shared_ptr<T> & ptr, std::uint32_t const nameid)
116  {
117  if(nameid & detail::msb2_32bit)
118  {
119  ar( memory_detail::make_ptr_wrapper(ptr) );
120  return true;
121  }
122  return false;
123  }
124 
126  template<class Archive, class T, class D> inline
127  typename std::enable_if<std::is_default_constructible<T>::value || traits::has_load_and_allocate<T, Archive>(), bool>::type
128  serialize_wrapper(Archive & ar, std::unique_ptr<T, D> & ptr, std::uint32_t const nameid)
129  {
130  if(nameid & detail::msb2_32bit)
131  {
132  ar( memory_detail::make_ptr_wrapper(ptr) );
133  return true;
134  }
135  return false;
136  }
137 
138  template<class Archive, class T> inline
139  typename std::enable_if<!std::is_default_constructible<T>::value && !traits::has_load_and_allocate<T, Archive>(), bool>::type
140  serialize_wrapper(Archive &, std::shared_ptr<T> &, std::uint32_t const nameid)
141  {
142  if(nameid & detail::msb2_32bit)
143  throw cereal::Exception("Cannot load a polymorphic type that is not default constructable and does not have a load_and_allocate function");
144  return false;
145  }
146 
147  template<class Archive, class T, class D> inline
148  typename std::enable_if<!std::is_default_constructible<T>::value && !traits::has_load_and_allocate<T, Archive>(), bool>::type
149  serialize_wrapper(Archive &, std::unique_ptr<T, D> &, std::uint32_t const nameid)
150  {
151  if(nameid & detail::msb2_32bit)
152  throw cereal::Exception("Cannot load a polymorphic type that is not default constructable and does not have a load_and_allocate function");
153  return false;
154  }
155  } // polymorphic_detail
156 
157  // ######################################################################
158  // Pointer serialization for polymorphic types
159 
161  template <class Archive, class T> inline
162  typename std::enable_if<std::is_polymorphic<T>::value, void>::type
163  save( Archive & ar, std::shared_ptr<T> const & ptr )
164  {
165  if(!ptr)
166  {
167  // same behavior as nullptr in memory implementation
168  ar( _CEREAL_NVP("id", std::uint32_t(0)) );
169  return;
170  }
171 
172  std::type_info const & ptrinfo = typeid(*ptr.get());
173  static std::type_info const & tinfo = typeid(T);
174 
175  if(ptrinfo == tinfo)
176  {
177  // The 2nd msb signals that the following pointer does not need to be
178  // cast with our polymorphic machinery
179  ar( _CEREAL_NVP("id", detail::msb2_32bit) );
180 
181  ar( _CEREAL_NVP("ptr_wrapper", memory_detail::make_ptr_wrapper(ptr)) );
182 
183  return;
184  }
185 
186  auto & bindingMap = detail::StaticObject<detail::OutputBindingMap<Archive>>::getInstance().map;
187 
188  auto binding = bindingMap.find(std::type_index(ptrinfo));
189  if(binding == bindingMap.end())
190  throw cereal::Exception("Trying to save an unregistered polymorphic type (" + cereal::util::demangle(ptrinfo.name()) + ")");
191 
192  binding->second.shared_ptr(&ar, ptr.get());
193  }
194 
196  template <class Archive, class T> inline
197  typename std::enable_if<std::is_polymorphic<T>::value, void>::type
198  load( Archive & ar, std::shared_ptr<T> & ptr )
199  {
200  std::uint32_t nameid;
201  ar( nameid );
202 
203  // Check to see if we can skip all of this polymorphism business
204  if(polymorphic_detail::serialize_wrapper(ar, ptr, nameid))
205  return;
206 
207  auto binding = polymorphic_detail::getInputBinding(ar, nameid);
208  std::shared_ptr<void> result;
209  binding.shared_ptr(&ar, result);
210  ptr = std::static_pointer_cast<T>(result);
211  }
212 
214  template <class Archive, class T> inline
215  typename std::enable_if<std::is_polymorphic<T>::value, void>::type
216  save( Archive & ar, std::weak_ptr<T> const & ptr )
217  {
218  auto const sptr = ptr.lock();
219  ar( _CEREAL_NVP("locked_ptr", sptr) );
220  }
221 
223  template <class Archive, class T> inline
224  typename std::enable_if<std::is_polymorphic<T>::value, void>::type
225  load( Archive & ar, std::weak_ptr<T> & ptr )
226  {
227  std::shared_ptr<T> sptr;
228  ar( sptr );
229  ptr = sptr;
230  }
231 
233  template <class Archive, class T, class D> inline
234  typename std::enable_if<std::is_polymorphic<T>::value, void>::type
235  save( Archive & ar, std::unique_ptr<T, D> const & ptr )
236  {
237  if(!ptr)
238  {
239  // same behavior as nullptr in memory implementation
240  ar( _CEREAL_NVP("id", std::uint32_t(0)) );
241  return;
242  }
243 
244  std::type_info const & ptrinfo = typeid(*ptr.get());
245  static std::type_info const & tinfo = typeid(T);
246 
247  if(ptrinfo == tinfo)
248  {
249  // The 2nd msb signals that the following pointer does not need to be
250  // cast with our polymorphic machinery
251  ar( _CEREAL_NVP("id", detail::msb2_32bit) );
252 
253  ar( _CEREAL_NVP("ptr_wrapper", memory_detail::make_ptr_wrapper(ptr)) );
254 
255  return;
256  }
257 
258  auto & bindingMap = detail::StaticObject<detail::OutputBindingMap<Archive>>::getInstance().map;
259 
260  auto binding = bindingMap.find(std::type_index(ptrinfo));
261  if(binding == bindingMap.end())
262  throw cereal::Exception("Trying to save an unregistered polymorphic type (" + cereal::util::demangle(ptrinfo.name()) + ")");
263 
264  binding->second.unique_ptr(&ar, ptr.get());
265  }
266 
268  template <class Archive, class T, class D> inline
269  typename std::enable_if<std::is_polymorphic<T>::value, void>::type
270  load( Archive & ar, std::unique_ptr<T, D> & ptr )
271  {
272  std::uint32_t nameid;
273  ar( nameid );
274 
275  // Check to see if we can skip all of this polymorphism business
276  if(polymorphic_detail::serialize_wrapper(ar, ptr, nameid))
277  return;
278 
279  auto binding = polymorphic_detail::getInputBinding(ar, nameid);
280  std::unique_ptr<void> result;
281  binding.unique_ptr(&ar, result);
282  ptr.reset(static_cast<T*>(result.release()));
283  }
284 } // namespace cereal
285 #endif // CEREAL_TYPES_POLYMORPHIC_HPP_