cereal
A C++11 library for serialization
 All Classes Files Functions Variables Typedefs Enumerations Groups
cereal.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_CEREAL_HPP_
28 #define CEREAL_CEREAL_HPP_
29 
30 #include <type_traits>
31 #include <unordered_map>
32 #include <unordered_set>
33 #include <cstddef>
34 #include <cstdint>
35 
36 #include <cereal/details/traits.hpp>
37 #include <cereal/details/helpers.hpp>
38 #include <cereal/types/base_class.hpp>
39 #include <cereal/types/common.hpp>
40 
41 namespace cereal
42 {
44 
45  // ######################################################################
47  struct Exception : public std::runtime_error
48  {
49  using std::runtime_error::runtime_error;
50  };
51 
52  // ######################################################################
54 
55  template <class T> inline
56  NameValuePair<T> make_nvp( std::string const & name, T && value )
57  {
58  return {name.c_str(), std::forward<T>(value)};
59  }
60 
62 
63  template <class T> inline
64  NameValuePair<T> make_nvp( const char * name, T && value )
65  {
66  return {name, std::forward<T>(value)};
67  }
68 
70 
71  #define CEREAL_NVP(T) ::cereal::make_nvp(#T, T)
72 
73  // ######################################################################
75 
78  template <class T> inline
79  BinaryData<T> binary_data( T && data, size_t size )
80  {
81  return {std::forward<T>(data), size};
82  }
83 
84  // ######################################################################
86 
90  template <class T>
91  SizeTag<T> make_size_tag( T && sz )
92  {
93  return {std::forward<T>(sz)};
94  }
95 
96  // ######################################################################
99 
102  template <class Archive, class T>
103  void prologue( Archive & /* archive */, T const & /* data */)
104  { }
105 
108  template <class Archive, class T>
109  void epilogue( Archive & /* archive */, T const & /* data */)
110  { }
111 
112  // ######################################################################
114 
124  enum Flags { AllowEmptyClassElision = 1 };
125 
126  // ######################################################################
128 
132  #define CEREAL_REGISTER_ARCHIVE(Archive) \
133  namespace cereal { namespace detail { \
134  template <class T> \
135  typename polymorphic_serialization_support<Archive, T>::type \
136  instantiate_polymorphic_binding( T*, Archive*, adl_tag ); \
137  } } // end namespaces
138 
140 
154  template<class ArchiveType, std::uint32_t Flags = 0>
156  {
157  public:
159 
160  OutputArchive(ArchiveType * const self) : self(self), itsCurrentPointerId(1), itsCurrentPolymorphicTypeId(1)
161  { }
162 
164  template <class ... Types> inline
165  ArchiveType & operator()( Types && ... args )
166  {
167  self->process( std::forward<Types>( args )... );
168  return *self;
169  }
170 
172 
178  std::uint32_t registerSharedPointer( void const * addr )
179  {
180  // Handle null pointers by just returning 0
181  if(addr == 0) return 0;
182 
183  auto id = itsSharedPointerMap.find( addr );
184  if( id == itsSharedPointerMap.end() )
185  {
186  auto ptrId = itsCurrentPointerId++;
187  itsSharedPointerMap.insert( {addr, ptrId} );
188  return ptrId | detail::msb_32bit; // mask MSB to be 1
189  }
190  else
191  return id->second;
192  }
193 
195 
201  std::uint32_t registerPolymorphicType( char const * name )
202  {
203  auto id = itsPolymorphicTypeMap.find( name );
204  if( id == itsPolymorphicTypeMap.end() )
205  {
206  auto polyId = itsCurrentPolymorphicTypeId++;
207  itsPolymorphicTypeMap.insert( {name, polyId} );
208  return polyId | detail::msb_32bit; // mask MSB to be 1
209  }
210  else
211  return id->second;
212  }
213 
214  private:
216  template <class T> inline
217  void process( T && head )
218  {
219  prologue( *self, head );
220  self->processImpl( head );
221  epilogue( *self, head );
222  }
223 
225  template <class T, class ... Other> inline
226  void process( T && head, Other && ... tail )
227  {
228  self->process( std::forward<T>( head ) );
229  self->process( std::forward<Other>( tail )... );
230  }
231 
233 
234  template <class T> inline
235  ArchiveType & processImpl(virtual_base_class<T> const & b)
236  {
237  traits::detail::base_class_id id(b.base_ptr);
238  if(itsBaseClassSet.count(id) == 0)
239  {
240  itsBaseClassSet.insert(id);
241  self->processImpl( *b.base_ptr );
242  }
243  return *self;
244  }
245 
247 
248  template <class T> inline
249  ArchiveType & processImpl(base_class<T> const & b)
250  {
251  self->processImpl( *b.base_ptr );
252  return *self;
253  }
254 
256  template <class T> inline
257  typename std::enable_if<traits::is_specialized_member_serialize<T, ArchiveType>() ||
258  (traits::is_output_serializable<T, ArchiveType>() && traits::has_member_serialize<T, ArchiveType>()),
259  ArchiveType &>::type
260  processImpl(T const & t)
261  {
262  access::member_serialize(*self, const_cast<T &>(t));
263  return *self;
264  }
265 
267  template <class T> inline
268  typename std::enable_if<traits::is_specialized_non_member_serialize<T, ArchiveType>() ||
269  (traits::is_output_serializable<T, ArchiveType>() && traits::has_non_member_serialize<T, ArchiveType>()),
270  ArchiveType &>::type
271  processImpl(T const & t)
272  {
273  serialize(*self, const_cast<T &>(t));
274  return *self;
275  }
276 
278  template <class T> inline
279  typename std::enable_if<traits::is_specialized_member_save<T, ArchiveType>() ||
280  (traits::is_output_serializable<T, ArchiveType>() && traits::has_member_save<T, ArchiveType>()),
281  ArchiveType &>::type
282  processImpl(T const & t)
283  {
284  access::member_save(*self, t);
285  return *self;
286  }
287 
289  template <class T> inline
290  typename std::enable_if<traits::is_specialized_non_member_save<T, ArchiveType>() ||
291  (traits::is_output_serializable<T, ArchiveType>() && traits::has_non_member_save<T, ArchiveType>()),
292  ArchiveType &>::type
293  processImpl(T const & t)
294  {
295  save(*self, t);
296  return *self;
297  }
298 
300  template <class T> inline
301  typename std::enable_if<(Flags & AllowEmptyClassElision) &&
302  !traits::is_output_serializable<T, ArchiveType>() && traits::is_empty_class<T>(), ArchiveType &>::type
303  processImpl(T const &)
304  {
305  return *self;
306  }
307 
309  template <class T> inline
310  typename std::enable_if<!traits::is_specialized<T, ArchiveType>() && !traits::is_output_serializable<T, ArchiveType>() &&
311  (!(Flags & AllowEmptyClassElision) || ((Flags & AllowEmptyClassElision) && !traits::is_empty_class<T>())),
312  ArchiveType &>::type
313  processImpl(T const &)
314  {
315  static_assert(traits::is_output_serializable<T, ArchiveType>(), "Trying to serialize an unserializable type with an output archive.\n\n"
316  "Types must either have a serialize function, or separate save/load functions (but not both).\n"
317  "Serialize functions generally have the following signature:\n\n"
318  "template<class Archive>\n"
319  " void serialize(Archive & ar)\n"
320  " {\n"
321  " ar( member1, member2, member3 );\n"
322  " }\n\n" );
323  return *self;
324  }
325 
326  private:
327  ArchiveType * const self;
328 
330  std::unordered_set<traits::detail::base_class_id, traits::detail::base_class_id_hash> itsBaseClassSet;
331 
333  std::unordered_map<void const *, std::uint32_t> itsSharedPointerMap;
334 
336  std::uint32_t itsCurrentPointerId;
337 
339  std::unordered_map<char const *, std::uint32_t> itsPolymorphicTypeMap;
340 
342  std::uint32_t itsCurrentPolymorphicTypeId;
343  }; // class OutputArchive
344 
345  // ######################################################################
347 
361  template<class ArchiveType, std::uint32_t Flags = 0>
363  {
364  public:
366 
367  InputArchive(ArchiveType * const self) : self(self) { }
368 
370  template <class ... Types> inline
371  ArchiveType & operator()( Types && ... args )
372  {
373  process( std::forward<Types>( args )... );
374  return *self;
375  }
376 
378 
383  std::shared_ptr<void> getSharedPointer(std::uint32_t const id)
384  {
385  if(id == 0) return std::shared_ptr<void>(nullptr);
386 
387  auto ptr = itsSharedPointerMap.find( id );
388  if(ptr == itsSharedPointerMap.end())
389  throw Exception("Error while trying to deserialize a smart pointer. Could not find id " + std::to_string(id));
390 
391  return ptr->second;
392  }
393 
395 
400  void registerSharedPointer(std::uint32_t const id, std::shared_ptr<void> ptr)
401  {
402  std::uint32_t const stripped_id = id & ~detail::msb_32bit;
403  itsSharedPointerMap.insert( {stripped_id, ptr} );
404  }
405 
407 
412  std::string getPolymorphicName(std::uint32_t const id)
413  {
414  auto name = itsPolymorphicTypeMap.find( id );
415  if(name == itsPolymorphicTypeMap.end())
416  {
417  throw Exception("Error while trying to deserialize a polymorphic pointer. Could not find type id " + std::to_string(id));
418  }
419  return name->second;
420  }
421 
423 
428  void registerPolymorphicName(std::uint32_t const id, std::string const & name)
429  {
430  std::uint32_t const stripped_id = id & ~detail::msb_32bit;
431  itsPolymorphicTypeMap.insert( {stripped_id, name} );
432  }
433 
434  private:
436  template <class T> inline
437  void process( T && head )
438  {
439  prologue( *self, head );
440  self->processImpl( head );
441  epilogue( *self, head );
442  }
443 
445  template <class T, class ... Other> inline
446  void process( T && head, Other && ... tail )
447  {
448  process( std::forward<T>( head ) );
449  process( std::forward<Other>( tail )... );
450  }
451 
453 
454  template <class T> inline
455  ArchiveType & processImpl(virtual_base_class<T> & b)
456  {
457  traits::detail::base_class_id id(b.base_ptr);
458  if(itsBaseClassSet.count(id) == 0)
459  {
460  itsBaseClassSet.insert(id);
461  self->processImpl( *b.base_ptr );
462  }
463  return *self;
464  }
465 
467 
468  template <class T> inline
469  ArchiveType & processImpl(base_class<T> & b)
470  {
471  self->processImpl( *b.base_ptr );
472  return *self;
473  }
474 
475 
477  template <class T> inline
478  typename std::enable_if<traits::is_specialized_member_serialize<T, ArchiveType>() ||
479  (traits::is_input_serializable<T, ArchiveType>() && traits::has_member_serialize<T, ArchiveType>()),
480  ArchiveType &>::type
481  processImpl(T & t)
482  {
483  access::member_serialize(*self, t);
484  return *self;
485  }
486 
488  template <class T> inline
489  typename std::enable_if<traits::is_specialized_non_member_serialize<T, ArchiveType>() ||
490  (traits::is_input_serializable<T, ArchiveType>() && traits::has_non_member_serialize<T, ArchiveType>()),
491  ArchiveType &>::type
492  processImpl(T & t)
493  {
494  serialize(*self, t);
495  return *self;
496  }
497 
499  template <class T> inline
500  typename std::enable_if<traits::is_specialized_member_load<T, ArchiveType>() ||
501  (traits::is_input_serializable<T, ArchiveType>() && traits::has_member_load<T, ArchiveType>()),
502  ArchiveType &>::type
503  processImpl(T & t)
504  {
505  access::member_load(*self, t);
506  return *self;
507  }
508 
510  template <class T> inline
511  typename std::enable_if<traits::is_specialized_non_member_load<T, ArchiveType>() ||
512  (traits::is_input_serializable<T, ArchiveType>() && traits::has_non_member_load<T, ArchiveType>()),
513  ArchiveType &>::type
514  processImpl(T & t)
515  {
516  load(*self, t);
517  return *self;
518  }
519 
521  template <class T> inline
522  typename std::enable_if<(Flags & AllowEmptyClassElision) &&
523  !traits::is_input_serializable<T, ArchiveType>() && traits::is_empty_class<T>(), ArchiveType &>::type
524  processImpl(T const &)
525  {
526  return *self;
527  }
528 
530  template <class T> inline
531  typename std::enable_if<!traits::is_specialized<T, ArchiveType>() && !traits::is_input_serializable<T, ArchiveType>() &&
532  (!(Flags & AllowEmptyClassElision) || ((Flags & AllowEmptyClassElision) && !traits::is_empty_class<T>())),
533  ArchiveType &>::type
534  processImpl(T const &)
535  {
536  static_assert(traits::is_output_serializable<T, ArchiveType>(), "Trying to serialize an unserializable type with an output archive.\n\n"
537  "Types must either have a serialize function, or separate save/load functions (but not both).\n"
538  "Serialize functions generally have the following signature:\n\n"
539  "template<class Archive>\n"
540  " void serialize(Archive & ar)\n"
541  " {\n"
542  " ar( member1, member2, member3 );\n"
543  " }\n\n" );
544  return *self;
545  }
546 
547  private:
548  ArchiveType * const self;
549 
551  std::unordered_set<traits::detail::base_class_id, traits::detail::base_class_id_hash> itsBaseClassSet;
552 
554  std::unordered_map<std::uint32_t, std::shared_ptr<void>> itsSharedPointerMap;
555 
557  std::unordered_map<std::uint32_t, std::string> itsPolymorphicTypeMap;
558  }; // class InputArchive
559 } // namespace cereal
560 
561 #endif // CEREAL_CEREAL_HPP_