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 the copyright holder 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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"
35
40
41#if defined(_MSC_VER) && _MSC_VER < 1916
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 CEREAL_DLL_EXPORT dynamic_init_dummy_##LibName(); \
172 } /* end detail */ \
173 } /* end cereal */ \
174 namespace { \
175 struct dynamic_init_##LibName { \
176 dynamic_init_##LibName() { \
177 ::cereal::detail::dynamic_init_dummy_##LibName(); \
178 } \
179 } dynamic_init_instance_##LibName; \
180 } /* end anonymous namespace */
181
182namespace cereal
183{
184 namespace polymorphic_detail
185 {
187
188 #define UNREGISTERED_POLYMORPHIC_EXCEPTION(LoadSave, Name) \
189 throw cereal::Exception("Trying to " #LoadSave " an unregistered polymorphic type (" + Name + ").\n" \
190 "Make sure your type is registered with CEREAL_REGISTER_TYPE and that the archive " \
191 "you are using was included (and registered with CEREAL_REGISTER_ARCHIVE) prior to calling CEREAL_REGISTER_TYPE.\n" \
192 "If your type is already registered and you still see this error, you may need to use CEREAL_REGISTER_DYNAMIC_INIT.");
193
195
196 template<class Archive> inline
197 typename ::cereal::detail::InputBindingMap<Archive>::Serializers getInputBinding(Archive & ar, std::uint32_t const nameid)
198 {
199 // If the nameid is zero, we serialized a null pointer
200 if(nameid == 0)
201 {
202 typename ::cereal::detail::InputBindingMap<Archive>::Serializers emptySerializers;
203 emptySerializers.shared_ptr = [](void*, std::shared_ptr<void> & ptr, std::type_info const &) { ptr.reset(); };
204 emptySerializers.unique_ptr = [](void*, std::unique_ptr<void, ::cereal::detail::EmptyDeleter<void>> & ptr, std::type_info const &) { ptr.reset( nullptr ); };
205 return emptySerializers;
206 }
207
208 std::string name;
209 if(nameid & detail::msb_32bit)
210 {
211 ar( CEREAL_NVP_("polymorphic_name", name) );
212 ar.registerPolymorphicName(nameid, name);
213 }
214 else
215 name = ar.getPolymorphicName(nameid);
216
217 auto const & bindingMap = detail::StaticObject<detail::InputBindingMap<Archive>>::getInstance().map;
218
219 auto binding = bindingMap.find(name);
220 if(binding == bindingMap.end())
222 return binding->second;
223 }
224
226
232 template<class Archive, class T> inline
233 typename std::enable_if<(traits::is_default_constructible<T>::value
235 && !std::is_abstract<T>::value, bool>::type
236 serialize_wrapper(Archive & ar, std::shared_ptr<T> & ptr, std::uint32_t const nameid)
237 {
238 if(nameid & detail::msb2_32bit)
239 {
240 ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper(ptr)) );
241 return true;
242 }
243 return false;
244 }
245
247
250 template<class Archive, class T, class D> inline
251 typename std::enable_if<(traits::is_default_constructible<T>::value
253 && !std::is_abstract<T>::value, bool>::type
254 serialize_wrapper(Archive & ar, std::unique_ptr<T, D> & ptr, std::uint32_t const nameid)
255 {
256 if(nameid & detail::msb2_32bit)
257 {
258 ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper(ptr)) );
259 return true;
260 }
261 return false;
262 }
263
265
270 template<class Archive, class T> inline
271 typename std::enable_if<(!traits::is_default_constructible<T>::value
273 || std::is_abstract<T>::value, bool>::type
274 serialize_wrapper(Archive &, std::shared_ptr<T> &, std::uint32_t const nameid)
275 {
276 if(nameid & detail::msb2_32bit)
277 throw cereal::Exception("Cannot load a polymorphic type that is not default constructable and does not have a load_and_construct function");
278 return false;
279 }
280
282
287 template<class Archive, class T, class D> inline
288 typename std::enable_if<(!traits::is_default_constructible<T>::value
290 || std::is_abstract<T>::value, bool>::type
291 serialize_wrapper(Archive &, std::unique_ptr<T, D> &, std::uint32_t const nameid)
292 {
293 if(nameid & detail::msb2_32bit)
294 throw cereal::Exception("Cannot load a polymorphic type that is not default constructable and does not have a load_and_construct function");
295 return false;
296 }
297 } // polymorphic_detail
298
299 // ######################################################################
300 // Pointer serialization for polymorphic types
301
303 template <class Archive, class T> inline
304 typename std::enable_if<std::is_polymorphic<T>::value && std::is_abstract<T>::value, void>::type
305 CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::shared_ptr<T> const & ptr )
306 {
307 if(!ptr)
308 {
309 // same behavior as nullptr in memory implementation
310 ar( CEREAL_NVP_("polymorphic_id", std::uint32_t(0)) );
311 return;
312 }
313
314 std::type_info const & ptrinfo = typeid(*ptr.get());
315 static std::type_info const & tinfo = typeid(T);
316 // ptrinfo can never be equal to T info since we can't have an instance
317 // of an abstract object
318 // this implies we need to do the lookup
319
320 auto const & bindingMap = detail::StaticObject<detail::OutputBindingMap<Archive>>::getInstance().map;
321
322 auto binding = bindingMap.find(std::type_index(ptrinfo));
323 if(binding == bindingMap.end())
324 UNREGISTERED_POLYMORPHIC_EXCEPTION(save, cereal::util::demangle(ptrinfo.name()))
325
326 binding->second.shared_ptr(&ar, ptr.get(), tinfo);
327 }
328
330 template <class Archive, class T> inline
331 typename std::enable_if<std::is_polymorphic<T>::value && !std::is_abstract<T>::value, void>::type
332 CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::shared_ptr<T> const & ptr )
333 {
334 if(!ptr)
335 {
336 // same behavior as nullptr in memory implementation
337 ar( CEREAL_NVP_("polymorphic_id", std::uint32_t(0)) );
338 return;
339 }
340
341 std::type_info const & ptrinfo = typeid(*ptr.get());
342 static std::type_info const & tinfo = typeid(T);
343
344 if(ptrinfo == tinfo)
345 {
346 // The 2nd msb signals that the following pointer does not need to be
347 // cast with our polymorphic machinery
348 ar( CEREAL_NVP_("polymorphic_id", detail::msb2_32bit) );
349
350 ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper(ptr)) );
351
352 return;
353 }
354
355 auto const & bindingMap = detail::StaticObject<detail::OutputBindingMap<Archive>>::getInstance().map;
356
357 auto binding = bindingMap.find(std::type_index(ptrinfo));
358 if(binding == bindingMap.end())
359 UNREGISTERED_POLYMORPHIC_EXCEPTION(save, cereal::util::demangle(ptrinfo.name()))
360
361 binding->second.shared_ptr(&ar, ptr.get(), tinfo);
362 }
363
365 template <class Archive, class T> inline
366 typename std::enable_if<std::is_polymorphic<T>::value, void>::type
367 CEREAL_LOAD_FUNCTION_NAME( Archive & ar, std::shared_ptr<T> & ptr )
368 {
369 std::uint32_t nameid;
370 ar( CEREAL_NVP_("polymorphic_id", nameid) );
371
372 // Check to see if we can skip all of this polymorphism business
373 if(polymorphic_detail::serialize_wrapper(ar, ptr, nameid))
374 return;
375
376 auto binding = polymorphic_detail::getInputBinding(ar, nameid);
377 std::shared_ptr<void> result;
378 binding.shared_ptr(&ar, result, typeid(T));
379 ptr = std::static_pointer_cast<T>(result);
380 }
381
383 template <class Archive, class T> inline
384 typename std::enable_if<std::is_polymorphic<T>::value, void>::type
385 CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::weak_ptr<T> const & ptr )
386 {
387 auto const sptr = ptr.lock();
388 ar( CEREAL_NVP_("locked_ptr", sptr) );
389 }
390
392 template <class Archive, class T> inline
393 typename std::enable_if<std::is_polymorphic<T>::value, void>::type
394 CEREAL_LOAD_FUNCTION_NAME( Archive & ar, std::weak_ptr<T> & ptr )
395 {
396 std::shared_ptr<T> sptr;
397 ar( CEREAL_NVP_("locked_ptr", sptr) );
398 ptr = sptr;
399 }
400
402 template <class Archive, class T, class D> inline
403 typename std::enable_if<std::is_polymorphic<T>::value && std::is_abstract<T>::value, void>::type
404 CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::unique_ptr<T, D> const & ptr )
405 {
406 if(!ptr)
407 {
408 // same behavior as nullptr in memory implementation
409 ar( CEREAL_NVP_("polymorphic_id", std::uint32_t(0)) );
410 return;
411 }
412
413 std::type_info const & ptrinfo = typeid(*ptr.get());
414 static std::type_info const & tinfo = typeid(T);
415 // ptrinfo can never be equal to T info since we can't have an instance
416 // of an abstract object
417 // this implies we need to do the lookup
418
419 auto const & bindingMap = detail::StaticObject<detail::OutputBindingMap<Archive>>::getInstance().map;
420
421 auto binding = bindingMap.find(std::type_index(ptrinfo));
422 if(binding == bindingMap.end())
423 UNREGISTERED_POLYMORPHIC_EXCEPTION(save, cereal::util::demangle(ptrinfo.name()))
424
425 binding->second.unique_ptr(&ar, ptr.get(), tinfo);
426 }
427
429 template <class Archive, class T, class D> inline
430 typename std::enable_if<std::is_polymorphic<T>::value && !std::is_abstract<T>::value, void>::type
431 CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::unique_ptr<T, D> const & ptr )
432 {
433 if(!ptr)
434 {
435 // same behavior as nullptr in memory implementation
436 ar( CEREAL_NVP_("polymorphic_id", std::uint32_t(0)) );
437 return;
438 }
439
440 std::type_info const & ptrinfo = typeid(*ptr.get());
441 static std::type_info const & tinfo = typeid(T);
442
443 if(ptrinfo == tinfo)
444 {
445 // The 2nd msb signals that the following pointer does not need to be
446 // cast with our polymorphic machinery
447 ar( CEREAL_NVP_("polymorphic_id", detail::msb2_32bit) );
448
449 ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper(ptr)) );
450
451 return;
452 }
453
454 auto const & bindingMap = detail::StaticObject<detail::OutputBindingMap<Archive>>::getInstance().map;
455
456 auto binding = bindingMap.find(std::type_index(ptrinfo));
457 if(binding == bindingMap.end())
458 UNREGISTERED_POLYMORPHIC_EXCEPTION(save, cereal::util::demangle(ptrinfo.name()))
459
460 binding->second.unique_ptr(&ar, ptr.get(), tinfo);
461 }
462
464 template <class Archive, class T, class D> inline
465 typename std::enable_if<std::is_polymorphic<T>::value, void>::type
466 CEREAL_LOAD_FUNCTION_NAME( Archive & ar, std::unique_ptr<T, D> & ptr )
467 {
468 std::uint32_t nameid;
469 ar( CEREAL_NVP_("polymorphic_id", nameid) );
470
471 // Check to see if we can skip all of this polymorphism business
472 if(polymorphic_detail::serialize_wrapper(ar, ptr, nameid))
473 return;
474
475 auto binding = polymorphic_detail::getInputBinding(ar, nameid);
476 std::unique_ptr<void, ::cereal::detail::EmptyDeleter<void>> result;
477 binding.unique_ptr(&ar, result, typeid(T));
478 ptr.reset(static_cast<T*>(result.release()));
479 }
480
481 #undef UNREGISTERED_POLYMORPHIC_EXCEPTION
482} // namespace cereal
483#endif // CEREAL_TYPES_POLYMORPHIC_HPP_
Main cereal functionality.
A static, pre-execution object.
Definition: static_object.hpp:68
Internal helper functionality.
#define CEREAL_NVP_(name, value)
Convenience for creating a templated NVP.
Definition: helpers.hpp:201
#define CEREAL_LOAD_FUNCTION_NAME
The deserialization (load) function name to search for.
Definition: macros.hpp:85
#define CEREAL_SAVE_FUNCTION_NAME
The serialization (save) function name to search for.
Definition: macros.hpp:92
Support for types found in <memory>
#define UNREGISTERED_POLYMORPHIC_EXCEPTION(LoadSave, Name)
Error message used for unregistered polymorphic types.
Definition: polymorphic.hpp:188
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:236
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:197
Internal polymorphism support.
An exception class thrown when things go wrong at runtime.
Definition: helpers.hpp:49
Non member load and construct check.
Definition: traits.hpp:942
Determines whether the class T can be default constructed by cereal::access.
Definition: traits.hpp:1255
Internal type trait support.
Internal misc utilities.