cereal
A C++11 library for serialization
 All Classes Files Functions Variables Typedefs Enumerations Groups
traits.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_DETAILS_TRAITS_HPP_
28 #define CEREAL_DETAILS_TRAITS_HPP_
29 
30 #include <type_traits>
31 #include <typeindex>
32 #include <memory>
33 
34 #include <cereal/access.hpp>
35 
36 namespace cereal
37 {
38  namespace traits
39  {
40  template<typename> struct Void { typedef void type; };
41 
42  // ######################################################################
43  // Member load_and_allocate
44  template<typename T, typename A>
45  bool constexpr has_member_load_and_allocate()
46  { return std::is_same<decltype( access::load_and_allocate<T>( std::declval<A&>() ) ), T*>::value; }
47 
48  // ######################################################################
49  // Non Member load_and_allocate
50  template<typename T, typename A>
51  bool constexpr has_non_member_load_and_allocate()
52  { return std::is_same<decltype( LoadAndAllocate<T>::load_and_allocate( std::declval<A&>() ) ), T*>::value; }
53 
54  // ######################################################################
55  // Has either a member or non member allocate
56  template<typename T, typename A>
57  bool constexpr has_load_and_allocate()
58  { return has_member_load_and_allocate<T, A>() || has_non_member_load_and_allocate<T, A>(); }
59 
60  // ######################################################################
61  // Member Serialize
62  template<typename T, class A, typename Sfinae = void>
63  struct has_member_serialize: std::false_type {};
64 
65  template<typename T, class A>
66  struct has_member_serialize< T, A,
67  typename Void<
68  decltype( access::member_serialize(std::declval<A&>(), std::declval<T&>() ) )
69  >::type
70  >: std::true_type {};
71 
72  // ######################################################################
73  // Non Member Serialize
74  char & serialize(...);
75  template<typename T, typename A>
76  bool constexpr has_non_member_serialize()
77  { return std::is_void<decltype(serialize(std::declval<A&>(), std::declval<T&>()))>::value; };
78 
79  // ######################################################################
80  // Member Load
81  template<typename T, class A, typename Sfinae = void>
82  struct has_member_load: std::false_type {};
83 
84  template<typename T, class A>
85  struct has_member_load< T, A,
86  typename Void<
87  decltype( access::member_load(std::declval<A&>(), std::declval<T&>() ) )
88  >::type
89  >: std::true_type {};
90 
91  // ######################################################################
92  // Non Member Load
93  char & load(...);
94  template<typename T, typename A>
95  bool constexpr has_non_member_load()
96  { return std::is_void<decltype(load(std::declval<A&>(), std::declval<T&>()))>::value; };
97 
98  // ######################################################################
99  // Member Save
100  template<typename T, class A, typename Sfinae = void>
101  struct has_member_save: std::false_type {};
102 
103  template<typename T, class A>
104  struct has_member_save< T, A,
105  typename Void<
106  decltype( access::member_save(std::declval<A&>(), std::declval<T const &>() ) )
107  >::type
108  >: std::true_type {};
109 
110  // ######################################################################
111  // Non-const Member Save
112  namespace detail
113  {
114  // Detection of any (const or non const) member save
115  template<typename T, class A, typename Sfinae = void>
116  struct has_member_save_any: std::false_type {};
117 
118  template<typename T, class A>
119  struct has_member_save_any< T, A,
120  typename Void<
121  decltype( access::non_const_member_save(std::declval<A&>(), std::declval<typename std::remove_const<T>::type &>() ) )
122  >::type
123  >: std::true_type {};
124  }
125 
126  // Returns true if we detect a member save function that is not const
127  template <class T, class A>
128  constexpr bool is_non_const_member_save()
129  {
131  }
132 
133  // ######################################################################
134  // Non Member Save
135  char & save(...);
136  template<typename T, typename A>
137  bool constexpr has_non_member_save()
138  { return std::is_void<decltype(save(std::declval<A&>(), std::declval<T const &>()))>::value; }
139 
140  // ######################################################################
141  // Non-const Non member Save
142  namespace detail
143  {
144  template<typename T, typename A>
145  bool constexpr has_non_member_save_any()
146  { return std::is_void<decltype(save(std::declval<A&>(), std::declval<typename std::remove_const<T>::type &>()))>::value; }
147  }
148 
149  // Returns true if we detect a non-member save function that is not const
150  template<typename T, typename A>
151  bool constexpr is_non_const_non_member_save()
152  { return !has_non_member_save<T, A>() && detail::has_non_member_save_any<T, A>(); }
153 
154  // ######################################################################
155  // Returns true if we have an invalid save function (non const)
156  template <class T, class A>
157  bool constexpr has_non_const_save()
158  { return is_non_const_member_save<T, A>() || is_non_const_non_member_save<T, A>(); }
159 
160  // ######################################################################
161  template <class T, class InputArchive, class OutputArchive>
162  constexpr bool has_member_split()
163  { return has_member_load<T, InputArchive>() && has_member_save<T, OutputArchive>(); }
164 
165  // ######################################################################
166  template <class T, class InputArchive, class OutputArchive>
167  constexpr bool has_non_member_split()
168  { return has_non_member_load<T, InputArchive>() && has_non_member_save<T, OutputArchive>(); }
169 
170  // ######################################################################
171  template <class T, class OutputArchive>
172  constexpr bool is_output_serializable()
173  {
174  static_assert( !has_non_const_save<T, OutputArchive>(),
175  "cereal detected a non const save. \n "
176  "save functions must either be const member functions or accept const type aguments if non-member" );
177 
178  return
179  has_member_save<T, OutputArchive>() ^
180  has_non_member_save<T, OutputArchive>() ^
181  has_member_serialize<T, OutputArchive>() ^
182  has_non_member_serialize<T, OutputArchive>();
183  }
184 
185  // ######################################################################
186  template <class T, class InputArchive>
187  constexpr bool is_input_serializable()
188  {
189  return
190  has_member_load<T, InputArchive>() ^
191  has_non_member_load<T, InputArchive>() ^
192  has_member_serialize<T, InputArchive>() ^
193  has_non_member_serialize<T, InputArchive>();
194  }
195 
196  // ######################################################################
197 
198  namespace detail
199  {
200  template <class T, class A>
201  constexpr auto is_specialized_member_serialize() -> bool
202  { return !std::is_base_of<std::false_type, specialize<A, T, specialization::member_serialize>>(); }
203 
204  template <class T, class A>
205  constexpr auto is_specialized_member_load_save() -> bool
206  { return !std::is_base_of<std::false_type, specialize<A, T, specialization::member_load_save>>(); }
207 
208  template <class T, class A>
209  constexpr auto is_specialized_non_member_serialize() -> bool
210  { return !std::is_base_of<std::false_type, specialize<A, T, specialization::non_member_serialize>>(); }
211 
212  template <class T, class A>
213  constexpr auto is_specialized_non_member_load_save() -> bool
214  { return !std::is_base_of<std::false_type, specialize<A, T, specialization::non_member_load_save>>(); }
215 
216  // Considered an error if specialization exists for more than one type
217  template <class T, class A>
218  constexpr auto is_specialized_error() -> bool
219  {
220  return (is_specialized_member_serialize<T, A>() +
221  is_specialized_member_load_save<T, A>() +
222  is_specialized_non_member_serialize<T, A>() +
223  is_specialized_non_member_load_save<T, A>()) <= 1;
224  }
225  } // namespace detail
226 
227  template <class T, class A>
228  constexpr auto is_specialized() -> bool
229  {
230  static_assert(detail::is_specialized_error<T, A>(), "More than one explicit specialization detected for type.");
231  return detail::is_specialized_member_serialize<T, A>() ||
232  detail::is_specialized_member_load_save<T, A>() ||
233  detail::is_specialized_non_member_serialize<T, A>() ||
234  detail::is_specialized_non_member_load_save<T, A>();
235  }
236 
237  template <class T, class A>
238  constexpr auto is_specialized_member_serialize() -> bool
239  {
240  static_assert( (is_specialized<T, A>() && detail::is_specialized_member_serialize<T, A>() && has_member_serialize<T, A>())
241  || !(is_specialized<T, A>() && detail::is_specialized_member_serialize<T, A>()),
242  "cereal detected member serialization specialization but no member serialize function" );
243  return is_specialized<T, A>() && detail::is_specialized_member_serialize<T, A>();
244  }
245 
246  template <class T, class A>
247  constexpr auto is_specialized_member_load() -> bool
248  {
249  static_assert( (is_specialized<T, A>() && detail::is_specialized_member_load_save<T, A>() && has_member_load<T, A>())
250  || !(is_specialized<T, A>() && detail::is_specialized_member_load_save<T, A>()),
251  "cereal detected member load specialization but no member load function" );
252  return is_specialized<T, A>() && detail::is_specialized_member_load_save<T, A>();
253  }
254 
255  template <class T, class A>
256  constexpr auto is_specialized_member_save() -> bool
257  {
258  static_assert( (is_specialized<T, A>() && detail::is_specialized_member_load_save<T, A>() && has_member_save<T, A>())
259  || !(is_specialized<T, A>() && detail::is_specialized_member_load_save<T, A>()),
260  "cereal detected member save specialization but no member save function" );
261  return is_specialized<T, A>() && detail::is_specialized_member_load_save<T, A>();
262  }
263 
264  template <class T, class A>
265  constexpr auto is_specialized_non_member_serialize() -> bool
266  {
267  static_assert( (is_specialized<T, A>() && detail::is_specialized_non_member_serialize<T, A>() && has_non_member_serialize<T, A>())
268  || !(is_specialized<T, A>() && detail::is_specialized_non_member_serialize<T, A>()),
269  "cereal detected non-member serialization specialization but no non-member serialize function" );
270  return is_specialized<T, A>() && detail::is_specialized_non_member_serialize<T, A>();
271  }
272 
273  template <class T, class A>
274  constexpr auto is_specialized_non_member_load() -> bool
275  {
276  static_assert( (is_specialized<T, A>() && detail::is_specialized_non_member_load_save<T, A>() && has_non_member_load<T, A>())
277  || !(is_specialized<T, A>() && detail::is_specialized_non_member_load_save<T, A>()),
278  "cereal detected non-member load specialization but no non-member load function" );
279  return is_specialized<T, A>() && detail::is_specialized_non_member_load_save<T, A>();
280  }
281 
282  template <class T, class A>
283  constexpr auto is_specialized_non_member_save() -> bool
284  {
285  static_assert( (is_specialized<T, A>() && detail::is_specialized_non_member_load_save<T, A>() && has_non_member_save<T, A>())
286  || !(is_specialized<T, A>() && detail::is_specialized_non_member_load_save<T, A>()),
287  "cereal detected non-member save specialization but no non-member save function" );
288  return is_specialized<T, A>() && detail::is_specialized_non_member_load_save<T, A>();
289  }
290 
291  // ######################################################################
292  template <class T>
293  constexpr size_t sizeof_array( size_t rank = std::rank<T>::value )
294  {
295  return rank == 0 ? 1 : std::extent<T>::value * sizeof_array<typename std::remove_extent<T>::type>( rank - 1 );
296  }
297 
298  // ######################################################################
299  namespace detail
300  {
301  template <class T, typename Enable = void>
303  { static constexpr bool value = false; };
304 
305  template <class T>
306  struct is_empty_class_impl<T, typename std::enable_if<std::is_class<T>::value>::type>
307  {
308  struct S : T
309  { uint8_t t; };
310 
311  static constexpr bool value = sizeof(S) == sizeof(uint8_t);
312  };
313 
315  {
316  template<class T>
317  base_class_id(T const * const t) :
318  type(typeid(T)),
319  ptr(t),
320  hash(std::hash<std::type_index>()(typeid(T)) ^ (std::hash<void const *>()(t) << 1))
321  { }
322 
323  bool operator==(base_class_id const & other) const
324  { return (type == other.type) && (ptr == other.ptr); }
325 
326  std::type_index type;
327  void const * ptr;
328  size_t hash;
329  };
330  struct base_class_id_hash { size_t operator()(base_class_id const & id) const { return id.hash; } };
331  }
332 
333  template<class T>
334  using is_empty_class = std::integral_constant<bool, detail::is_empty_class_impl<T>::value>;
335 
336  // ######################################################################
338 
357  #define CEREAL_ARCHIVE_RESTRICT(INTYPE, OUTTYPE) \
358  typename std::enable_if<std::is_same<Archive, INTYPE>::value || std::is_same<Archive, OUTTYPE>::value, void>::type
359  } // namespace traits
360 
361  namespace detail
362  {
363  template <class T, class A, bool Member = traits::has_member_load_and_allocate<T, A>(), bool NonMember = traits::has_non_member_load_and_allocate<T, A>()>
364  struct Load
365  {
366  static_assert( !sizeof(T), "Cereal detected both member and non member load_and_allocate functions!" );
367  static T * load_andor_allocate( A & ar )
368  { return nullptr; }
369  };
370 
371  template <class T, class A>
372  struct Load<T, A, false, false>
373  {
374  static_assert( std::is_default_constructible<T>::value,
375  "Trying to serialize a an object with no default constructor.\n\n"
376  "Types must either be default constructible or define either a member or non member Construct function.\n"
377  "Construct functions generally have the signature:\n\n"
378  "template <class Archive>\n"
379  "static T * load_and_allocate(Archive & ar)\n"
380  "{\n"
381  " var a;\n"
382  " ar & a\n"
383  " return new T(a);\n"
384  "}\n\n" );
385  static T * load_andor_allocate( A & ar )
386  { return new T(); }
387  };
388 
389  template <class T, class A>
390  struct Load<T, A, true, false>
391  {
392  static T * load_andor_allocate( A & ar )
393  {
394  return access::load_and_allocate<T>( ar );
395  }
396  };
397 
398  template <class T, class A>
399  struct Load<T, A, false, true>
400  {
401  static T * load_andor_allocate( A & ar )
402  {
404  }
405  };
406  } // namespace detail
407 } // namespace cereal
408 
409 #endif // CEREAL_DETAILS_TRAITS_HPP_