Line data Source code
1 : /*! \file traits.hpp
2 : \brief Internal type trait support
3 : \ingroup Internal */
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_DETAILS_TRAITS_HPP_
31 : #define CEREAL_DETAILS_TRAITS_HPP_
32 :
33 : #ifndef __clang__
34 : #if (__GNUC__ == 4 && __GNUC_MINOR__ <= 7)
35 : #define CEREAL_OLDER_GCC
36 : #endif // gcc 4.7 or earlier
37 : #endif // __clang__
38 :
39 : #include <type_traits>
40 : #include <typeindex>
41 :
42 : #include "cereal/macros.hpp"
43 : #include "cereal/access.hpp"
44 :
45 : namespace cereal
46 : {
47 : namespace traits
48 : {
49 : using yes = std::true_type;
50 : using no = std::false_type;
51 :
52 : namespace detail
53 : {
54 : // ######################################################################
55 : //! Used to delay a static_assert until template instantiation
56 : template <class T>
57 : struct delay_static_assert : std::false_type {};
58 :
59 : // ######################################################################
60 : // SFINAE Helpers
61 : #ifdef CEREAL_OLDER_GCC // when VS supports better SFINAE, we can use this as the default
62 : template<typename> struct Void { typedef void type; };
63 : #endif // CEREAL_OLDER_GCC
64 :
65 : //! Return type for SFINAE Enablers
66 : enum class sfinae {};
67 :
68 : // ######################################################################
69 : // Helper functionality for boolean integral constants and Enable/DisableIf
70 : template <bool H, bool ... T> struct meta_bool_and : std::integral_constant<bool, H && meta_bool_and<T...>::value> {};
71 : template <bool B> struct meta_bool_and<B> : std::integral_constant<bool, B> {};
72 :
73 : template <bool H, bool ... T> struct meta_bool_or : std::integral_constant<bool, H || meta_bool_or<T...>::value> {};
74 : template <bool B> struct meta_bool_or<B> : std::integral_constant<bool, B> {};
75 :
76 : // workaround needed due to bug in MSVC 2013, see
77 : // http://connect.microsoft.com/VisualStudio/feedback/details/800231/c-11-alias-template-issue
78 : template <bool ... Conditions>
79 : struct EnableIfHelper : std::enable_if<meta_bool_and<Conditions...>::value, sfinae> {};
80 :
81 : template <bool ... Conditions>
82 : struct DisableIfHelper : std::enable_if<!meta_bool_or<Conditions...>::value, sfinae> {};
83 : } // namespace detail
84 :
85 : //! Used as the default value for EnableIf and DisableIf template parameters
86 : /*! @relates EnableIf
87 : @relates DisableIf */
88 : static const detail::sfinae sfinae = {};
89 :
90 : // ######################################################################
91 : //! Provides a way to enable a function if conditions are met
92 : /*! This is intended to be used in a near identical fashion to std::enable_if
93 : while being significantly easier to read at the cost of not allowing for as
94 : complicated of a condition.
95 :
96 : This will compile (allow the function) if every condition evaluates to true.
97 : at compile time. This should be used with SFINAE to ensure that at least
98 : one other candidate function works when one fails due to an EnableIf.
99 :
100 : This should be used as the las template parameter to a function as
101 : an unnamed parameter with a default value of cereal::traits::sfinae:
102 :
103 : @code{cpp}
104 : // using by making the last template argument variadic
105 : template <class T, EnableIf<std::is_same<T, bool>::value> = sfinae>
106 : void func(T t );
107 : @endcode
108 :
109 : Note that this performs a logical AND of all conditions, so you will need
110 : to construct more complicated requirements with this fact in mind.
111 :
112 : @relates DisableIf
113 : @relates sfinae
114 : @tparam Conditions The conditions which will be logically ANDed to enable the function. */
115 : template <bool ... Conditions>
116 : using EnableIf = typename detail::EnableIfHelper<Conditions...>::type;
117 :
118 : // ######################################################################
119 : //! Provides a way to disable a function if conditions are met
120 : /*! This is intended to be used in a near identical fashion to std::enable_if
121 : while being significantly easier to read at the cost of not allowing for as
122 : complicated of a condition.
123 :
124 : This will compile (allow the function) if every condition evaluates to false.
125 : This should be used with SFINAE to ensure that at least one other candidate
126 : function works when one fails due to a DisableIf.
127 :
128 : This should be used as the las template parameter to a function as
129 : an unnamed parameter with a default value of cereal::traits::sfinae:
130 :
131 : @code{cpp}
132 : // using by making the last template argument variadic
133 : template <class T, DisableIf<std::is_same<T, bool>::value> = sfinae>
134 : void func(T t );
135 : @endcode
136 :
137 : This is often used in conjunction with EnableIf to form an enable/disable pair of
138 : overloads.
139 :
140 : Note that this performs a logical AND of all conditions, so you will need
141 : to construct more complicated requirements with this fact in mind. If all conditions
142 : hold, the function will be disabled.
143 :
144 : @relates EnableIf
145 : @relates sfinae
146 : @tparam Conditions The conditions which will be logically ANDed to disable the function. */
147 : template <bool ... Conditions>
148 : using DisableIf = typename detail::DisableIfHelper<Conditions...>::type;
149 :
150 : // ######################################################################
151 : namespace detail
152 : {
153 : template <class InputArchive>
154 : struct get_output_from_input : no
155 : {
156 : static_assert( detail::delay_static_assert<InputArchive>::value,
157 : "Could not find an associated output archive for input archive." );
158 : };
159 :
160 : template <class OutputArchive>
161 : struct get_input_from_output : no
162 : {
163 : static_assert( detail::delay_static_assert<OutputArchive>::value,
164 : "Could not find an associated input archive for output archive." );
165 : };
166 : }
167 :
168 : //! Sets up traits that relate an input archive to an output archive
169 : #define CEREAL_SETUP_ARCHIVE_TRAITS(InputArchive, OutputArchive) \
170 : namespace cereal { namespace traits { namespace detail { \
171 : template <> struct get_output_from_input<InputArchive> \
172 : { using type = OutputArchive; }; \
173 : template <> struct get_input_from_output<OutputArchive> \
174 : { using type = InputArchive; }; } } } /* end namespaces */
175 :
176 : // ######################################################################
177 : //! Used to convert a MAKE_HAS_XXX macro into a versioned variant
178 : #define CEREAL_MAKE_VERSIONED_TEST ,0
179 :
180 : // ######################################################################
181 : //! Creates a test for whether a non const member function exists
182 : /*! This creates a class derived from std::integral_constant that will be true if
183 : the type has the proper member function for the given archive.
184 :
185 : @param name The name of the function to test for (e.g. serialize, load, save)
186 : @param test_name The name to give the test for the function being tested for (e.g. serialize, versioned_serialize)
187 : @param versioned Either blank or the macro CEREAL_MAKE_VERSIONED_TEST */
188 : #ifdef CEREAL_OLDER_GCC
189 : #define CEREAL_MAKE_HAS_MEMBER_TEST(name, test_name, versioned) \
190 : template <class T, class A, class SFINAE = void> \
191 : struct has_member_##test_name : no {}; \
192 : template <class T, class A> \
193 : struct has_member_##test_name<T, A, \
194 : typename detail::Void< decltype( cereal::access::member_##name( std::declval<A&>(), std::declval<T&>() versioned ) ) >::type> : yes {}
195 : #else // NOT CEREAL_OLDER_GCC
196 : #define CEREAL_MAKE_HAS_MEMBER_TEST(name, test_name, versioned) \
197 : namespace detail \
198 : { \
199 : template <class T, class A> \
200 : struct has_member_##name##_##versioned##_impl \
201 : { \
202 : template <class TT, class AA> \
203 : static auto test(int) -> decltype( cereal::access::member_##name( std::declval<AA&>(), std::declval<TT&>() versioned ), yes()); \
204 : template <class, class> \
205 : static no test(...); \
206 : static const bool value = std::is_same<decltype(test<T, A>(0)), yes>::value; \
207 : }; \
208 : } /* end namespace detail */ \
209 : template <class T, class A> \
210 : struct has_member_##test_name : std::integral_constant<bool, detail::has_member_##name##_##versioned##_impl<T, A>::value> {}
211 : #endif // NOT CEREAL_OLDER_GCC
212 :
213 : // ######################################################################
214 : //! Creates a test for whether a non const non-member function exists
215 : /*! This creates a class derived from std::integral_constant that will be true if
216 : the type has the proper non-member function for the given archive. */
217 : #define CEREAL_MAKE_HAS_NON_MEMBER_TEST(test_name, func, versioned) \
218 : namespace detail \
219 : { \
220 : template <class T, class A> \
221 : struct has_non_member_##test_name##_impl \
222 : { \
223 : template <class TT, class AA> \
224 : static auto test(int) -> decltype( func( std::declval<AA&>(), std::declval<TT&>() versioned ), yes()); \
225 : template <class, class> \
226 : static no test( ... ); \
227 : static const bool value = std::is_same<decltype( test<T, A>( 0 ) ), yes>::value; \
228 : }; \
229 : } /* end namespace detail */ \
230 : template <class T, class A> \
231 : struct has_non_member_##test_name : std::integral_constant<bool, detail::has_non_member_##test_name##_impl<T, A>::value> {}
232 :
233 : // ######################################################################
234 : // Member Serialize
235 : CEREAL_MAKE_HAS_MEMBER_TEST(serialize, serialize,);
236 :
237 : // ######################################################################
238 : // Member Serialize (versioned)
239 : CEREAL_MAKE_HAS_MEMBER_TEST(serialize, versioned_serialize, CEREAL_MAKE_VERSIONED_TEST);
240 :
241 : // ######################################################################
242 : // Non Member Serialize
243 : CEREAL_MAKE_HAS_NON_MEMBER_TEST(serialize, CEREAL_SERIALIZE_FUNCTION_NAME,);
244 :
245 : // ######################################################################
246 : // Non Member Serialize (versioned)
247 : CEREAL_MAKE_HAS_NON_MEMBER_TEST(versioned_serialize, CEREAL_SERIALIZE_FUNCTION_NAME, CEREAL_MAKE_VERSIONED_TEST);
248 :
249 : // ######################################################################
250 : // Member Load
251 : CEREAL_MAKE_HAS_MEMBER_TEST(load, load,);
252 :
253 : // ######################################################################
254 : // Member Load (versioned)
255 : CEREAL_MAKE_HAS_MEMBER_TEST(load, versioned_load, CEREAL_MAKE_VERSIONED_TEST);
256 :
257 : // ######################################################################
258 : // Non Member Load
259 : CEREAL_MAKE_HAS_NON_MEMBER_TEST(load, CEREAL_LOAD_FUNCTION_NAME,);
260 :
261 : // ######################################################################
262 : // Non Member Load (versioned)
263 : CEREAL_MAKE_HAS_NON_MEMBER_TEST(versioned_load, CEREAL_LOAD_FUNCTION_NAME, CEREAL_MAKE_VERSIONED_TEST);
264 :
265 : // ######################################################################
266 : #undef CEREAL_MAKE_HAS_NON_MEMBER_TEST
267 : #undef CEREAL_MAKE_HAS_MEMBER_TEST
268 :
269 : // ######################################################################
270 : //! Creates a test for whether a member save function exists
271 : /*! This creates a class derived from std::integral_constant that will be true if
272 : the type has the proper member function for the given archive.
273 :
274 : @param test_name The name to give the test (e.g. save or versioned_save)
275 : @param versioned Either blank or the macro CEREAL_MAKE_VERSIONED_TEST */
276 : #ifdef CEREAL_OLDER_GCC
277 : #define CEREAL_MAKE_HAS_MEMBER_SAVE_IMPL(test_name, versioned) \
278 : namespace detail \
279 : { \
280 : template <class T, class A> \
281 : struct has_member_##test_name##_impl \
282 : { \
283 : template <class TT, class AA, class SFINAE = void> struct test : no {}; \
284 : template <class TT, class AA> \
285 : struct test<TT, AA, \
286 : typename detail::Void< decltype( cereal::access::member_save( std::declval<AA&>(), \
287 : std::declval<TT const &>() versioned ) ) >::type> : yes {}; \
288 : static const bool value = test<T, A>(); \
289 : \
290 : template <class TT, class AA, class SFINAE = void> struct test2 : no {}; \
291 : template <class TT, class AA> \
292 : struct test2<TT, AA, \
293 : typename detail::Void< decltype( cereal::access::member_save_non_const( \
294 : std::declval<AA&>(), \
295 : std::declval<typename std::remove_const<TT>::type&>() versioned ) ) >::type> : yes {}; \
296 : static const bool not_const_type = test2<T, A>(); \
297 : }; \
298 : } /* end namespace detail */
299 : #else /* NOT CEREAL_OLDER_GCC =================================== */
300 : #define CEREAL_MAKE_HAS_MEMBER_SAVE_IMPL(test_name, versioned) \
301 : namespace detail \
302 : { \
303 : template <class T, class A> \
304 : struct has_member_##test_name##_impl \
305 : { \
306 : template <class TT, class AA> \
307 : static auto test(int) -> decltype( cereal::access::member_save( std::declval<AA&>(), \
308 : std::declval<TT const &>() versioned ), yes()); \
309 : template <class, class> static no test(...); \
310 : static const bool value = std::is_same<decltype(test<T, A>(0)), yes>::value; \
311 : \
312 : template <class TT, class AA> \
313 : static auto test2(int) -> decltype( cereal::access::member_save_non_const( \
314 : std::declval<AA &>(), \
315 : std::declval<typename std::remove_const<TT>::type&>() versioned ), yes()); \
316 : template <class, class> static no test2(...); \
317 : static const bool not_const_type = std::is_same<decltype(test2<T, A>(0)), yes>::value; \
318 : }; \
319 : } /* end namespace detail */
320 : #endif /* NOT CEREAL_OLDER_GCC */
321 :
322 : // ######################################################################
323 : // Member Save
324 : CEREAL_MAKE_HAS_MEMBER_SAVE_IMPL(save, )
325 :
326 : template <class T, class A>
327 : struct has_member_save : std::integral_constant<bool, detail::has_member_save_impl<T, A>::value>
328 : {
329 : typedef typename detail::has_member_save_impl<T, A> check;
330 : static_assert( check::value || !check::not_const_type,
331 : "cereal detected a non-const save. \n "
332 : "save member functions must always be const" );
333 : };
334 :
335 : // ######################################################################
336 : // Member Save (versioned)
337 : CEREAL_MAKE_HAS_MEMBER_SAVE_IMPL(versioned_save, CEREAL_MAKE_VERSIONED_TEST)
338 :
339 : template <class T, class A>
340 : struct has_member_versioned_save : std::integral_constant<bool, detail::has_member_versioned_save_impl<T, A>::value>
341 : {
342 : typedef typename detail::has_member_versioned_save_impl<T, A> check;
343 : static_assert( check::value || !check::not_const_type,
344 : "cereal detected a versioned non-const save. \n "
345 : "save member functions must always be const" );
346 : };
347 :
348 : // ######################################################################
349 : #undef CEREAL_MAKE_HAS_MEMBER_SAVE_IMPL
350 :
351 : // ######################################################################
352 : //! Creates a test for whether a non-member save function exists
353 : /*! This creates a class derived from std::integral_constant that will be true if
354 : the type has the proper non-member function for the given archive.
355 :
356 : @param test_name The name to give the test (e.g. save or versioned_save)
357 : @param versioned Either blank or the macro CEREAL_MAKE_VERSIONED_TEST */
358 : #define CEREAL_MAKE_HAS_NON_MEMBER_SAVE_TEST(test_name, versioned) \
359 : namespace detail \
360 : { \
361 : template <class T, class A> \
362 : struct has_non_member_##test_name##_impl \
363 : { \
364 : template <class TT, class AA> \
365 : static auto test(int) -> decltype( CEREAL_SAVE_FUNCTION_NAME( \
366 : std::declval<AA&>(), \
367 : std::declval<TT const &>() versioned ), yes()); \
368 : template <class, class> static no test(...); \
369 : static const bool value = std::is_same<decltype(test<T, A>(0)), yes>::value; \
370 : \
371 : template <class TT, class AA> \
372 : static auto test2(int) -> decltype( CEREAL_SAVE_FUNCTION_NAME( \
373 : std::declval<AA &>(), \
374 : std::declval<typename std::remove_const<TT>::type&>() versioned ), yes()); \
375 : template <class, class> static no test2(...); \
376 : static const bool not_const_type = std::is_same<decltype(test2<T, A>(0)), yes>::value; \
377 : }; \
378 : } /* end namespace detail */ \
379 : \
380 : template <class T, class A> \
381 : struct has_non_member_##test_name : std::integral_constant<bool, detail::has_non_member_##test_name##_impl<T, A>::value> \
382 : { \
383 : using check = typename detail::has_non_member_##test_name##_impl<T, A>; \
384 : static_assert( check::value || !check::not_const_type, \
385 : "cereal detected a non-const type parameter in non-member " #test_name ". \n " \
386 : #test_name " non-member functions must always pass their types as const" ); \
387 : };
388 :
389 : // ######################################################################
390 : // Non Member Save
391 : CEREAL_MAKE_HAS_NON_MEMBER_SAVE_TEST(save, )
392 :
393 : // ######################################################################
394 : // Non Member Save (versioned)
395 : CEREAL_MAKE_HAS_NON_MEMBER_SAVE_TEST(versioned_save, CEREAL_MAKE_VERSIONED_TEST)
396 :
397 : // ######################################################################
398 : #undef CEREAL_MAKE_HAS_NON_MEMBER_SAVE_TEST
399 :
400 : // ######################################################################
401 : // Minimal Utilities
402 : namespace detail
403 : {
404 : // Determines if the provided type is an std::string
405 : template <class> struct is_string : std::false_type {};
406 :
407 : template <class CharT, class Traits, class Alloc>
408 : struct is_string<std::basic_string<CharT, Traits, Alloc>> : std::true_type {};
409 : }
410 :
411 : // Determines if the type is valid for use with a minimal serialize function
412 : template <class T>
413 : struct is_minimal_type : std::integral_constant<bool,
414 : detail::is_string<T>::value || std::is_arithmetic<T>::value> {};
415 :
416 : // ######################################################################
417 : //! Creates implementation details for whether a member save_minimal function exists
418 : /*! This creates a class derived from std::integral_constant that will be true if
419 : the type has the proper member function for the given archive.
420 :
421 : @param test_name The name to give the test (e.g. save_minimal or versioned_save_minimal)
422 : @param versioned Either blank or the macro CEREAL_MAKE_VERSIONED_TEST */
423 : #ifdef CEREAL_OLDER_GCC
424 : #define CEREAL_MAKE_HAS_MEMBER_SAVE_MINIMAL_IMPL(test_name, versioned) \
425 : namespace detail \
426 : { \
427 : template <class T, class A> \
428 : struct has_member_##test_name##_impl \
429 : { \
430 : template <class TT, class AA, class SFINAE = void> struct test : no {}; \
431 : template <class TT, class AA> \
432 : struct test<TT, AA, typename detail::Void< decltype( \
433 : cereal::access::member_save_minimal( std::declval<AA const &>(), \
434 : std::declval<TT const &>() versioned ) ) >::type> : yes {}; \
435 : \
436 : static const bool value = test<T, A>(); \
437 : \
438 : template <class TT, class AA, class SFINAE = void> struct test2 : no {}; \
439 : template <class TT, class AA> \
440 : struct test2<TT, AA, typename detail::Void< decltype( \
441 : cereal::access::member_save_minimal_non_const( std::declval<AA const &>(), \
442 : std::declval<typename std::remove_const<TT>::type&>() versioned ) ) >::type> : yes {}; \
443 : static const bool not_const_type = test2<T, A>(); \
444 : \
445 : static const bool valid = value || !not_const_type; \
446 : }; \
447 : } /* end namespace detail */
448 : #else /* NOT CEREAL_OLDER_GCC =================================== */
449 : #define CEREAL_MAKE_HAS_MEMBER_SAVE_MINIMAL_IMPL(test_name, versioned) \
450 : namespace detail \
451 : { \
452 : template <class T, class A> \
453 : struct has_member_##test_name##_impl \
454 : { \
455 : template <class TT, class AA> \
456 : static auto test(int) -> decltype( cereal::access::member_save_minimal( \
457 : std::declval<AA const &>(), \
458 : std::declval<TT const &>() versioned ), yes()); \
459 : template <class, class> static no test(...); \
460 : static const bool value = std::is_same<decltype(test<T, A>(0)), yes>::value; \
461 : \
462 : template <class TT, class AA> \
463 : static auto test2(int) -> decltype( cereal::access::member_save_minimal_non_const( \
464 : std::declval<AA const &>(), \
465 : std::declval<typename std::remove_const<TT>::type&>() versioned ), yes()); \
466 : template <class, class> static no test2(...); \
467 : static const bool not_const_type = std::is_same<decltype(test2<T, A>(0)), yes>::value; \
468 : \
469 : static const bool valid = value || !not_const_type; \
470 : }; \
471 : } /* end namespace detail */
472 : #endif // NOT CEREAL_OLDER_GCC
473 :
474 : // ######################################################################
475 : //! Creates helpers for minimal save functions
476 : /*! The get_member_*_type structs allow access to the return type of a save_minimal,
477 : assuming that the function actually exists. If the function does not
478 : exist, the type will be void.
479 :
480 : @param test_name The name to give the test (e.g. save_minimal or versioned_save_minimal)
481 : @param versioned Either blank or the macro CEREAL_MAKE_VERSIONED_TEST */
482 : #define CEREAL_MAKE_HAS_MEMBER_SAVE_MINIMAL_HELPERS_IMPL(test_name, versioned) \
483 : namespace detail \
484 : { \
485 : template <class T, class A, bool Valid> \
486 : struct get_member_##test_name##_type { using type = void; }; \
487 : \
488 : template <class T, class A> \
489 : struct get_member_##test_name##_type<T, A, true> \
490 : { \
491 : using type = decltype( cereal::access::member_save_minimal( std::declval<A const &>(), \
492 : std::declval<T const &>() versioned ) ); \
493 : }; \
494 : } /* end namespace detail */
495 :
496 : // ######################################################################
497 : //! Creates a test for whether a member save_minimal function exists
498 : /*! This creates a class derived from std::integral_constant that will be true if
499 : the type has the proper member function for the given archive.
500 :
501 : @param test_name The name to give the test (e.g. save_minimal or versioned_save_minimal) */
502 : #define CEREAL_MAKE_HAS_MEMBER_SAVE_MINIMAL_TEST(test_name) \
503 : template <class T, class A> \
504 : struct has_member_##test_name : std::integral_constant<bool, detail::has_member_##test_name##_impl<T, A>::value> \
505 : { \
506 : using check = typename detail::has_member_##test_name##_impl<T, A>; \
507 : static_assert( check::valid, \
508 : "cereal detected a non-const member " #test_name ". \n " \
509 : #test_name " member functions must always be const" ); \
510 : \
511 : using type = typename detail::get_member_##test_name##_type<T, A, check::value>::type; \
512 : static_assert( (check::value && is_minimal_type<type>::value) || !check::value, \
513 : "cereal detected a member " #test_name " with an invalid return type. \n " \
514 : "return type must be arithmetic or string" ); \
515 : };
516 :
517 : // ######################################################################
518 : // Member Save Minimal
519 : CEREAL_MAKE_HAS_MEMBER_SAVE_MINIMAL_IMPL(save_minimal, )
520 : CEREAL_MAKE_HAS_MEMBER_SAVE_MINIMAL_HELPERS_IMPL(save_minimal, )
521 : CEREAL_MAKE_HAS_MEMBER_SAVE_MINIMAL_TEST(save_minimal)
522 :
523 : // ######################################################################
524 : // Member Save Minimal (versioned)
525 : CEREAL_MAKE_HAS_MEMBER_SAVE_MINIMAL_IMPL(versioned_save_minimal, CEREAL_MAKE_VERSIONED_TEST)
526 : CEREAL_MAKE_HAS_MEMBER_SAVE_MINIMAL_HELPERS_IMPL(versioned_save_minimal, CEREAL_MAKE_VERSIONED_TEST)
527 : CEREAL_MAKE_HAS_MEMBER_SAVE_MINIMAL_TEST(versioned_save_minimal)
528 :
529 : // ######################################################################
530 : #undef CEREAL_MAKE_HAS_MEMBER_SAVE_MINIMAL_IMPL
531 : #undef CEREAL_MAKE_HAS_MEMBER_SAVE_MINIMAL_HELPERS_IMPL
532 : #undef CEREAL_MAKE_HAS_MEMBER_SAVE_MINIMAL_TEST
533 :
534 : // ######################################################################
535 : //! Creates a test for whether a non-member save_minimal function exists
536 : /*! This creates a class derived from std::integral_constant that will be true if
537 : the type has the proper member function for the given archive.
538 :
539 : @param test_name The name to give the test (e.g. save_minimal or versioned_save_minimal)
540 : @param versioned Either blank or the macro CEREAL_MAKE_VERSIONED_TEST */
541 : #define CEREAL_MAKE_HAS_NON_MEMBER_SAVE_MINIMAL_TEST(test_name, versioned) \
542 : namespace detail \
543 : { \
544 : template <class T, class A> \
545 : struct has_non_member_##test_name##_impl \
546 : { \
547 : template <class TT, class AA> \
548 : static auto test(int) -> decltype( CEREAL_SAVE_MINIMAL_FUNCTION_NAME( \
549 : std::declval<AA const &>(), \
550 : std::declval<TT const &>() versioned ), yes()); \
551 : template <class, class> static no test(...); \
552 : static const bool value = std::is_same<decltype(test<T, A>(0)), yes>::value; \
553 : \
554 : template <class TT, class AA> \
555 : static auto test2(int) -> decltype( CEREAL_SAVE_MINIMAL_FUNCTION_NAME( \
556 : std::declval<AA const &>(), \
557 : std::declval<typename std::remove_const<TT>::type&>() versioned ), yes()); \
558 : template <class, class> static no test2(...); \
559 : static const bool not_const_type = std::is_same<decltype(test2<T, A>(0)), yes>::value; \
560 : \
561 : static const bool valid = value || !not_const_type; \
562 : }; \
563 : \
564 : template <class T, class A, bool Valid> \
565 : struct get_non_member_##test_name##_type { using type = void; }; \
566 : \
567 : template <class T, class A> \
568 : struct get_non_member_##test_name##_type <T, A, true> \
569 : { \
570 : using type = decltype( CEREAL_SAVE_MINIMAL_FUNCTION_NAME( std::declval<A const &>(), \
571 : std::declval<T const &>() versioned ) ); \
572 : }; \
573 : } /* end namespace detail */ \
574 : \
575 : template <class T, class A> \
576 : struct has_non_member_##test_name : std::integral_constant<bool, detail::has_non_member_##test_name##_impl<T, A>::value> \
577 : { \
578 : using check = typename detail::has_non_member_##test_name##_impl<T, A>; \
579 : static_assert( check::valid, \
580 : "cereal detected a non-const type parameter in non-member " #test_name ". \n " \
581 : #test_name " non-member functions must always pass their types as const" ); \
582 : \
583 : using type = typename detail::get_non_member_##test_name##_type<T, A, check::value>::type; \
584 : static_assert( (check::value && is_minimal_type<type>::value) || !check::value, \
585 : "cereal detected a non-member " #test_name " with an invalid return type. \n " \
586 : "return type must be arithmetic or string" ); \
587 : };
588 :
589 : // ######################################################################
590 : // Non-Member Save Minimal
591 : CEREAL_MAKE_HAS_NON_MEMBER_SAVE_MINIMAL_TEST(save_minimal, )
592 :
593 : // ######################################################################
594 : // Non-Member Save Minimal (versioned)
595 : CEREAL_MAKE_HAS_NON_MEMBER_SAVE_MINIMAL_TEST(versioned_save_minimal, CEREAL_MAKE_VERSIONED_TEST)
596 :
597 : // ######################################################################
598 : #undef CEREAL_MAKE_HAS_NON_MEMBER_SAVE_MINIMAL_TEST
599 :
600 : // ######################################################################
601 : // Load Minimal Utilities
602 : namespace detail
603 : {
604 : //! Used to help strip away conversion wrappers
605 : /*! If someone writes a non-member load/save minimal function that accepts its
606 : parameter as some generic template type and needs to perform trait checks
607 : on that type, our NoConvert wrappers will interfere with this. Using
608 : the struct strip_minmal, users can strip away our wrappers to get to
609 : the underlying type, allowing traits to work properly */
610 : struct NoConvertBase {};
611 :
612 : //! A struct that prevents implicit conversion
613 : /*! Any type instantiated with this struct will be unable to implicitly convert
614 : to another type. Is designed to only allow conversion to Source const &.
615 :
616 : @tparam Source the type of the original source */
617 : template <class Source>
618 : struct NoConvertConstRef : NoConvertBase
619 : {
620 : using type = Source; //!< Used to get underlying type easily
621 :
622 : template <class Dest, class = typename std::enable_if<std::is_same<Source, Dest>::value>::type>
623 : operator Dest () = delete;
624 :
625 : //! only allow conversion if the types are the same and we are converting into a const reference
626 : template <class Dest, class = typename std::enable_if<std::is_same<Source, Dest>::value>::type>
627 : operator Dest const & ();
628 : };
629 :
630 : //! A struct that prevents implicit conversion
631 : /*! Any type instantiated with this struct will be unable to implicitly convert
632 : to another type. Is designed to only allow conversion to Source &.
633 :
634 : @tparam Source the type of the original source */
635 : template <class Source>
636 : struct NoConvertRef : NoConvertBase
637 : {
638 : using type = Source; //!< Used to get underlying type easily
639 :
640 : template <class Dest, class = typename std::enable_if<std::is_same<Source, Dest>::value>::type>
641 : operator Dest () = delete;
642 :
643 : #ifdef __clang__
644 : template <class Dest, class = typename std::enable_if<std::is_same<Source, Dest>::value>::type>
645 : operator Dest const & () = delete;
646 : #endif // __clang__
647 :
648 : //! only allow conversion if the types are the same and we are converting into a const reference
649 : template <class Dest, class = typename std::enable_if<std::is_same<Source, Dest>::value>::type>
650 : operator Dest & ();
651 : };
652 :
653 : //! A type that can implicitly convert to anything else
654 : struct AnyConvert
655 : {
656 : template <class Dest>
657 : operator Dest & ();
658 :
659 : template <class Dest>
660 : operator Dest const & () const;
661 : };
662 : } // namespace detail
663 :
664 : // ######################################################################
665 : //! Creates a test for whether a member load_minimal function exists
666 : /*! This creates a class derived from std::integral_constant that will be true if
667 : the type has the proper member function for the given archive.
668 :
669 : Our strategy here is to first check if a function matching the signature more or less exists
670 : (allow anything like load_minimal(xxx) using AnyConvert, and then secondly enforce
671 : that it has the correct signature using NoConvertConstRef
672 :
673 : @param test_name The name to give the test (e.g. load_minimal or versioned_load_minimal)
674 : @param versioned Either blank or the macro CEREAL_MAKE_VERSIONED_TEST */
675 : #ifdef CEREAL_OLDER_GCC
676 : #define CEREAL_MAKE_HAS_MEMBER_LOAD_MINIMAL_IMPL(test_name, versioned) \
677 : namespace detail \
678 : { \
679 : template <class T, class A, class SFINAE = void> struct has_member_##test_name##_impl : no {}; \
680 : template <class T, class A> \
681 : struct has_member_##test_name##_impl<T, A, typename detail::Void< decltype( \
682 : cereal::access::member_load_minimal( std::declval<A const &>(), \
683 : std::declval<T &>(), AnyConvert() versioned ) ) >::type> : yes {}; \
684 : \
685 : template <class T, class A, class U, class SFINAE = void> struct has_member_##test_name##_type_impl : no {}; \
686 : template <class T, class A, class U> \
687 : struct has_member_##test_name##_type_impl<T, A, U, typename detail::Void< decltype( \
688 : cereal::access::member_load_minimal( std::declval<A const &>(), \
689 : std::declval<T &>(), NoConvertConstRef<U>() versioned ) ) >::type> : yes {}; \
690 : } /* end namespace detail */
691 : #else /* NOT CEREAL_OLDER_GCC =================================== */
692 : #define CEREAL_MAKE_HAS_MEMBER_LOAD_MINIMAL_IMPL(test_name, versioned) \
693 : namespace detail \
694 : { \
695 : template <class T, class A> \
696 : struct has_member_##test_name##_impl \
697 : { \
698 : template <class TT, class AA> \
699 : static auto test(int) -> decltype( cereal::access::member_load_minimal( \
700 : std::declval<AA const &>(), \
701 : std::declval<TT &>(), AnyConvert() versioned ), yes()); \
702 : template <class, class> static no test(...); \
703 : static const bool value = std::is_same<decltype(test<T, A>(0)), yes>::value; \
704 : }; \
705 : template <class T, class A, class U> \
706 : struct has_member_##test_name##_type_impl \
707 : { \
708 : template <class TT, class AA, class UU> \
709 : static auto test(int) -> decltype( cereal::access::member_load_minimal( \
710 : std::declval<AA const &>(), \
711 : std::declval<TT &>(), NoConvertConstRef<UU>() versioned ), yes()); \
712 : template <class, class, class> static no test(...); \
713 : static const bool value = std::is_same<decltype(test<T, A, U>(0)), yes>::value; \
714 : \
715 : }; \
716 : } /* end namespace detail */
717 : #endif // NOT CEREAL_OLDER_GCC
718 :
719 : // ######################################################################
720 : //! Creates helpers for minimal load functions
721 : /*! The has_member_*_wrapper structs ensure that the load and save types for the
722 : requested function type match appropriately.
723 :
724 : @param load_test_name The name to give the test (e.g. load_minimal or versioned_load_minimal)
725 : @param save_test_name The name to give the test (e.g. save_minimal or versioned_save_minimal,
726 : should match the load name.
727 : @param save_test_prefix The name to give the test (e.g. save_minimal or versioned_save_minimal,
728 : should match the load name, without the trailing "_minimal" (e.g.
729 : save or versioned_save). Needed because the preprocessor is an abomination.
730 : @param versioned Either blank or the macro CEREAL_MAKE_VERSIONED_TEST */
731 : #define CEREAL_MAKE_HAS_MEMBER_LOAD_MINIMAL_HELPERS_IMPL(load_test_name, save_test_name, save_test_prefix, versioned) \
732 : namespace detail \
733 : { \
734 : template <class T, class A, bool Valid> \
735 : struct has_member_##load_test_name##_wrapper : std::false_type {}; \
736 : \
737 : template <class T, class A> \
738 : struct has_member_##load_test_name##_wrapper<T, A, true> \
739 : { \
740 : using AOut = typename detail::get_output_from_input<A>::type; \
741 : \
742 : static_assert( has_member_##save_test_prefix##_minimal<T, AOut>::value, \
743 : "cereal detected member " #load_test_name " but no valid member " #save_test_name ". \n " \
744 : "cannot evaluate correctness of " #load_test_name " without valid " #save_test_name "." ); \
745 : \
746 : using SaveType = typename detail::get_member_##save_test_prefix##_minimal_type<T, AOut, true>::type; \
747 : const static bool value = has_member_##load_test_name##_impl<T, A>::value; \
748 : const static bool valid = has_member_##load_test_name##_type_impl<T, A, SaveType>::value; \
749 : \
750 : static_assert( valid || !value, "cereal detected different or invalid types in corresponding member " \
751 : #load_test_name " and " #save_test_name " functions. \n " \
752 : "the paramater to " #load_test_name " must be a constant reference to the type that " \
753 : #save_test_name " returns." ); \
754 : }; \
755 : } /* end namespace detail */
756 :
757 : // ######################################################################
758 : //! Creates a test for whether a member load_minimal function exists
759 : /*! This creates a class derived from std::integral_constant that will be true if
760 : the type has the proper member function for the given archive.
761 :
762 : @param load_test_name The name to give the test (e.g. load_minimal or versioned_load_minimal)
763 : @param load_test_prefix The above parameter minus the trailing "_minimal" */
764 : #define CEREAL_MAKE_HAS_MEMBER_LOAD_MINIMAL_TEST(load_test_name, load_test_prefix) \
765 : template <class T, class A> \
766 : struct has_member_##load_test_prefix##_minimal : std::integral_constant<bool, \
767 : detail::has_member_##load_test_name##_wrapper<T, A, detail::has_member_##load_test_name##_impl<T, A>::value>::value> {};
768 :
769 : // ######################################################################
770 : // Member Load Minimal
771 : CEREAL_MAKE_HAS_MEMBER_LOAD_MINIMAL_IMPL(load_minimal, )
772 : CEREAL_MAKE_HAS_MEMBER_LOAD_MINIMAL_HELPERS_IMPL(load_minimal, save_minimal, save, )
773 : CEREAL_MAKE_HAS_MEMBER_LOAD_MINIMAL_TEST(load_minimal, load)
774 :
775 : // ######################################################################
776 : // Member Load Minimal (versioned)
777 : CEREAL_MAKE_HAS_MEMBER_LOAD_MINIMAL_IMPL(versioned_load_minimal, CEREAL_MAKE_VERSIONED_TEST)
778 : CEREAL_MAKE_HAS_MEMBER_LOAD_MINIMAL_HELPERS_IMPL(versioned_load_minimal, versioned_save_minimal, versioned_save, CEREAL_MAKE_VERSIONED_TEST)
779 : CEREAL_MAKE_HAS_MEMBER_LOAD_MINIMAL_TEST(versioned_load_minimal, versioned_load)
780 :
781 : // ######################################################################
782 : #undef CEREAL_MAKE_HAS_MEMBER_LOAD_MINIMAL_IMPL
783 : #undef CEREAL_MAKE_HAS_MEMBER_LOAD_MINIMAL_HELPERS_IMPL
784 : #undef CEREAL_MAKE_HAS_MEMBER_LOAD_MINIMAL_TEST
785 :
786 : // ######################################################################
787 : // Non-Member Load Minimal
788 : namespace detail
789 : {
790 : #ifdef CEREAL_OLDER_GCC
791 : void CEREAL_LOAD_MINIMAL_FUNCTION_NAME(); // prevents nonsense complaining about not finding this
792 : void CEREAL_SAVE_MINIMAL_FUNCTION_NAME();
793 : #endif // CEREAL_OLDER_GCC
794 : } // namespace detail
795 :
796 : // ######################################################################
797 : //! Creates a test for whether a non-member load_minimal function exists
798 : /*! This creates a class derived from std::integral_constant that will be true if
799 : the type has the proper member function for the given archive.
800 :
801 : See notes from member load_minimal implementation.
802 :
803 : Note that there should be an additional const check on load_minimal after the valid check,
804 : but this currently interferes with many valid uses of minimal serialization. It has been
805 : removed (see #565 on github) and previously was:
806 :
807 : @code
808 : static_assert( check::const_valid || !check::exists,
809 : "cereal detected an invalid serialization type parameter in non-member " #test_name ". "
810 : #test_name " non-member functions must accept their serialization type by non-const reference" );
811 : @endcode
812 :
813 : See #132, #436, #263, and #565 on https://github.com/USCiLab/cereal for more details.
814 :
815 : @param test_name The name to give the test (e.g. load_minimal or versioned_load_minimal)
816 : @param save_name The corresponding name the save test would have (e.g. save_minimal or versioned_save_minimal)
817 : @param versioned Either blank or the macro CEREAL_MAKE_VERSIONED_TEST */
818 : #define CEREAL_MAKE_HAS_NON_MEMBER_LOAD_MINIMAL_TEST(test_name, save_name, versioned) \
819 : namespace detail \
820 : { \
821 : template <class T, class A, class U = void> \
822 : struct has_non_member_##test_name##_impl \
823 : { \
824 : template <class TT, class AA> \
825 : static auto test(int) -> decltype( CEREAL_LOAD_MINIMAL_FUNCTION_NAME( \
826 : std::declval<AA const &>(), std::declval<TT&>(), AnyConvert() versioned ), yes() ); \
827 : template <class, class> static no test( ... ); \
828 : static const bool exists = std::is_same<decltype( test<T, A>( 0 ) ), yes>::value; \
829 : \
830 : template <class TT, class AA, class UU> \
831 : static auto test2(int) -> decltype( CEREAL_LOAD_MINIMAL_FUNCTION_NAME( \
832 : std::declval<AA const &>(), std::declval<TT&>(), NoConvertConstRef<UU>() versioned ), yes() ); \
833 : template <class, class, class> static no test2( ... ); \
834 : static const bool valid = std::is_same<decltype( test2<T, A, U>( 0 ) ), yes>::value; \
835 : \
836 : template <class TT, class AA> \
837 : static auto test3(int) -> decltype( CEREAL_LOAD_MINIMAL_FUNCTION_NAME( \
838 : std::declval<AA const &>(), NoConvertRef<TT>(), AnyConvert() versioned ), yes() ); \
839 : template <class, class> static no test3( ... ); \
840 : static const bool const_valid = std::is_same<decltype( test3<T, A>( 0 ) ), yes>::value; \
841 : }; \
842 : \
843 : template <class T, class A, bool Valid> \
844 : struct has_non_member_##test_name##_wrapper : std::false_type {}; \
845 : \
846 : template <class T, class A> \
847 : struct has_non_member_##test_name##_wrapper<T, A, true> \
848 : { \
849 : using AOut = typename detail::get_output_from_input<A>::type; \
850 : \
851 : static_assert( detail::has_non_member_##save_name##_impl<T, AOut>::valid, \
852 : "cereal detected non-member " #test_name " but no valid non-member " #save_name ". \n " \
853 : "cannot evaluate correctness of " #test_name " without valid " #save_name "." ); \
854 : \
855 : using SaveType = typename detail::get_non_member_##save_name##_type<T, AOut, true>::type; \
856 : using check = has_non_member_##test_name##_impl<T, A, SaveType>; \
857 : static const bool value = check::exists; \
858 : \
859 : static_assert( check::valid || !check::exists, "cereal detected different types in corresponding non-member " \
860 : #test_name " and " #save_name " functions. \n " \
861 : "the paramater to " #test_name " must be a constant reference to the type that " #save_name " returns." ); \
862 : }; \
863 : } /* namespace detail */ \
864 : \
865 : template <class T, class A> \
866 : struct has_non_member_##test_name : std::integral_constant<bool, \
867 : detail::has_non_member_##test_name##_wrapper<T, A, detail::has_non_member_##test_name##_impl<T, A>::exists>::value> {};
868 :
869 : // ######################################################################
870 : // Non-Member Load Minimal
871 : CEREAL_MAKE_HAS_NON_MEMBER_LOAD_MINIMAL_TEST(load_minimal, save_minimal, )
872 :
873 : // ######################################################################
874 : // Non-Member Load Minimal (versioned)
875 : CEREAL_MAKE_HAS_NON_MEMBER_LOAD_MINIMAL_TEST(versioned_load_minimal, versioned_save_minimal, CEREAL_MAKE_VERSIONED_TEST)
876 :
877 : // ######################################################################
878 : #undef CEREAL_MAKE_HAS_NON_MEMBER_LOAD_MINIMAL_TEST
879 :
880 : // ######################################################################
881 : namespace detail
882 : {
883 : // const stripped away before reaching here, prevents errors on conversion from
884 : // construct<const T> to construct<T>
885 : template<typename T, typename A>
886 : struct has_member_load_and_construct_impl : std::integral_constant<bool,
887 : std::is_same<decltype( access::load_and_construct<T>( std::declval<A&>(), std::declval< ::cereal::construct<T>&>() ) ), void>::value>
888 : { };
889 :
890 : template<typename T, typename A>
891 : struct has_member_versioned_load_and_construct_impl : std::integral_constant<bool,
892 : std::is_same<decltype( access::load_and_construct<T>( std::declval<A&>(), std::declval< ::cereal::construct<T>&>(), 0 ) ), void>::value>
893 : { };
894 : } // namespace detail
895 :
896 : //! Member load and construct check
897 : template<typename T, typename A>
898 : struct has_member_load_and_construct : detail::has_member_load_and_construct_impl<typename std::remove_const<T>::type, A>
899 : { };
900 :
901 : //! Member load and construct check (versioned)
902 : template<typename T, typename A>
903 : struct has_member_versioned_load_and_construct : detail::has_member_versioned_load_and_construct_impl<typename std::remove_const<T>::type, A>
904 : { };
905 :
906 : // ######################################################################
907 : //! Creates a test for whether a non-member load_and_construct specialization exists
908 : /*! This creates a class derived from std::integral_constant that will be true if
909 : the type has the proper non-member function for the given archive. */
910 : #define CEREAL_MAKE_HAS_NON_MEMBER_LOAD_AND_CONSTRUCT_TEST(test_name, versioned) \
911 : namespace detail \
912 : { \
913 : template <class T, class A> \
914 : struct has_non_member_##test_name##_impl \
915 : { \
916 : template <class TT, class AA> \
917 : static auto test(int) -> decltype( LoadAndConstruct<TT>::load_and_construct( \
918 : std::declval<AA&>(), std::declval< ::cereal::construct<TT>&>() versioned ), yes()); \
919 : template <class, class> \
920 : static no test( ... ); \
921 : static const bool value = std::is_same<decltype( test<T, A>( 0 ) ), yes>::value; \
922 : }; \
923 : } /* end namespace detail */ \
924 : template <class T, class A> \
925 : struct has_non_member_##test_name : \
926 : std::integral_constant<bool, detail::has_non_member_##test_name##_impl<typename std::remove_const<T>::type, A>::value> {};
927 :
928 : // ######################################################################
929 : //! Non member load and construct check
930 : CEREAL_MAKE_HAS_NON_MEMBER_LOAD_AND_CONSTRUCT_TEST(load_and_construct, )
931 :
932 : // ######################################################################
933 : //! Non member load and construct check (versioned)
934 : CEREAL_MAKE_HAS_NON_MEMBER_LOAD_AND_CONSTRUCT_TEST(versioned_load_and_construct, CEREAL_MAKE_VERSIONED_TEST)
935 :
936 : // ######################################################################
937 : //! Has either a member or non member load and construct
938 : template<typename T, typename A>
939 : struct has_load_and_construct : std::integral_constant<bool,
940 : has_member_load_and_construct<T, A>::value || has_non_member_load_and_construct<T, A>::value ||
941 : has_member_versioned_load_and_construct<T, A>::value || has_non_member_versioned_load_and_construct<T, A>::value>
942 : { };
943 :
944 : // ######################################################################
945 : #undef CEREAL_MAKE_HAS_NON_MEMBER_LOAD_AND_CONSTRUCT_TEST
946 :
947 : // ######################################################################
948 : // End of serialization existence tests
949 : #undef CEREAL_MAKE_VERSIONED_TEST
950 :
951 : // ######################################################################
952 : template <class T, class InputArchive, class OutputArchive>
953 : struct has_member_split : std::integral_constant<bool,
954 : (has_member_load<T, InputArchive>::value && has_member_save<T, OutputArchive>::value) ||
955 : (has_member_versioned_load<T, InputArchive>::value && has_member_versioned_save<T, OutputArchive>::value)> {};
956 :
957 : // ######################################################################
958 : template <class T, class InputArchive, class OutputArchive>
959 : struct has_non_member_split : std::integral_constant<bool,
960 : (has_non_member_load<T, InputArchive>::value && has_non_member_save<T, OutputArchive>::value) ||
961 : (has_non_member_versioned_load<T, InputArchive>::value && has_non_member_versioned_save<T, OutputArchive>::value)> {};
962 :
963 : // ######################################################################
964 : template <class T, class OutputArchive>
965 : struct has_invalid_output_versioning : std::integral_constant<bool,
966 : (has_member_versioned_save<T, OutputArchive>::value && has_member_save<T, OutputArchive>::value) ||
967 : (has_non_member_versioned_save<T, OutputArchive>::value && has_non_member_save<T, OutputArchive>::value) ||
968 : (has_member_versioned_serialize<T, OutputArchive>::value && has_member_serialize<T, OutputArchive>::value) ||
969 : (has_non_member_versioned_serialize<T, OutputArchive>::value && has_non_member_serialize<T, OutputArchive>::value) ||
970 : (has_member_versioned_save_minimal<T, OutputArchive>::value && has_member_save_minimal<T, OutputArchive>::value) ||
971 : (has_non_member_versioned_save_minimal<T, OutputArchive>::value && has_non_member_save_minimal<T, OutputArchive>::value)> {};
972 :
973 : // ######################################################################
974 : template <class T, class InputArchive>
975 : struct has_invalid_input_versioning : std::integral_constant<bool,
976 : (has_member_versioned_load<T, InputArchive>::value && has_member_load<T, InputArchive>::value) ||
977 : (has_non_member_versioned_load<T, InputArchive>::value && has_non_member_load<T, InputArchive>::value) ||
978 : (has_member_versioned_serialize<T, InputArchive>::value && has_member_serialize<T, InputArchive>::value) ||
979 : (has_non_member_versioned_serialize<T, InputArchive>::value && has_non_member_serialize<T, InputArchive>::value) ||
980 : (has_member_versioned_load_minimal<T, InputArchive>::value && has_member_load_minimal<T, InputArchive>::value) ||
981 : (has_non_member_versioned_load_minimal<T, InputArchive>::value && has_non_member_load_minimal<T, InputArchive>::value)> {};
982 :
983 : // ######################################################################
984 : namespace detail
985 : {
986 : //! Create a test for a cereal::specialization entry
987 : #define CEREAL_MAKE_IS_SPECIALIZED_IMPL(name) \
988 : template <class T, class A> \
989 : struct is_specialized_##name : std::integral_constant<bool, \
990 : !std::is_base_of<std::false_type, specialize<A, T, specialization::name>>::value> {}
991 :
992 : CEREAL_MAKE_IS_SPECIALIZED_IMPL(member_serialize);
993 : CEREAL_MAKE_IS_SPECIALIZED_IMPL(member_load_save);
994 : CEREAL_MAKE_IS_SPECIALIZED_IMPL(member_load_save_minimal);
995 : CEREAL_MAKE_IS_SPECIALIZED_IMPL(non_member_serialize);
996 : CEREAL_MAKE_IS_SPECIALIZED_IMPL(non_member_load_save);
997 : CEREAL_MAKE_IS_SPECIALIZED_IMPL(non_member_load_save_minimal);
998 :
999 : #undef CEREAL_MAKE_IS_SPECIALIZED_IMPL
1000 :
1001 : //! Number of specializations detected
1002 : template <class T, class A>
1003 : struct count_specializations : std::integral_constant<int,
1004 : is_specialized_member_serialize<T, A>::value +
1005 : is_specialized_member_load_save<T, A>::value +
1006 : is_specialized_member_load_save_minimal<T, A>::value +
1007 : is_specialized_non_member_serialize<T, A>::value +
1008 : is_specialized_non_member_load_save<T, A>::value +
1009 : is_specialized_non_member_load_save_minimal<T, A>::value> {};
1010 : } // namespace detail
1011 :
1012 : //! Check if any specialization exists for a type
1013 : template <class T, class A>
1014 : struct is_specialized : std::integral_constant<bool,
1015 : detail::is_specialized_member_serialize<T, A>::value ||
1016 : detail::is_specialized_member_load_save<T, A>::value ||
1017 : detail::is_specialized_member_load_save_minimal<T, A>::value ||
1018 : detail::is_specialized_non_member_serialize<T, A>::value ||
1019 : detail::is_specialized_non_member_load_save<T, A>::value ||
1020 : detail::is_specialized_non_member_load_save_minimal<T, A>::value>
1021 : {
1022 : static_assert(detail::count_specializations<T, A>::value <= 1, "More than one explicit specialization detected for type.");
1023 : };
1024 :
1025 : //! Create the static assertion for some specialization
1026 : /*! This assertion will fail if the type is indeed specialized and does not have the appropriate
1027 : type of serialization functions */
1028 : #define CEREAL_MAKE_IS_SPECIALIZED_ASSERT(name, versioned_name, print_name, spec_name) \
1029 : static_assert( (is_specialized<T, A>::value && detail::is_specialized_##spec_name<T, A>::value && \
1030 : (has_##name<T, A>::value || has_##versioned_name<T, A>::value)) \
1031 : || !(is_specialized<T, A>::value && detail::is_specialized_##spec_name<T, A>::value), \
1032 : "cereal detected " #print_name " specialization but no " #print_name " serialize function" )
1033 :
1034 : //! Generates a test for specialization for versioned and unversioned functions
1035 : /*! This creates checks that can be queried to see if a given type of serialization function
1036 : has been specialized for this type */
1037 : #define CEREAL_MAKE_IS_SPECIALIZED(name, versioned_name, spec_name) \
1038 : template <class T, class A> \
1039 : struct is_specialized_##name : std::integral_constant<bool, \
1040 : is_specialized<T, A>::value && detail::is_specialized_##spec_name<T, A>::value> \
1041 : { CEREAL_MAKE_IS_SPECIALIZED_ASSERT(name, versioned_name, name, spec_name); }; \
1042 : template <class T, class A> \
1043 : struct is_specialized_##versioned_name : std::integral_constant<bool, \
1044 : is_specialized<T, A>::value && detail::is_specialized_##spec_name<T, A>::value> \
1045 : { CEREAL_MAKE_IS_SPECIALIZED_ASSERT(name, versioned_name, versioned_name, spec_name); }
1046 :
1047 : CEREAL_MAKE_IS_SPECIALIZED(member_serialize, member_versioned_serialize, member_serialize);
1048 : CEREAL_MAKE_IS_SPECIALIZED(non_member_serialize, non_member_versioned_serialize, non_member_serialize);
1049 :
1050 : CEREAL_MAKE_IS_SPECIALIZED(member_save, member_versioned_save, member_load_save);
1051 : CEREAL_MAKE_IS_SPECIALIZED(non_member_save, non_member_versioned_save, non_member_load_save);
1052 : CEREAL_MAKE_IS_SPECIALIZED(member_load, member_versioned_load, member_load_save);
1053 : CEREAL_MAKE_IS_SPECIALIZED(non_member_load, non_member_versioned_load, non_member_load_save);
1054 :
1055 : CEREAL_MAKE_IS_SPECIALIZED(member_save_minimal, member_versioned_save_minimal, member_load_save_minimal);
1056 : CEREAL_MAKE_IS_SPECIALIZED(non_member_save_minimal, non_member_versioned_save_minimal, non_member_load_save_minimal);
1057 : CEREAL_MAKE_IS_SPECIALIZED(member_load_minimal, member_versioned_load_minimal, member_load_save_minimal);
1058 : CEREAL_MAKE_IS_SPECIALIZED(non_member_load_minimal, non_member_versioned_load_minimal, non_member_load_save_minimal);
1059 :
1060 : #undef CEREAL_MAKE_IS_SPECIALIZED_ASSERT
1061 : #undef CEREAL_MAKE_IS_SPECIALIZED
1062 :
1063 : // ######################################################################
1064 : // detects if a type has any active minimal output serialization
1065 : template <class T, class OutputArchive>
1066 : struct has_minimal_output_serialization : std::integral_constant<bool,
1067 : is_specialized_member_save_minimal<T, OutputArchive>::value ||
1068 : ((has_member_save_minimal<T, OutputArchive>::value ||
1069 : has_non_member_save_minimal<T, OutputArchive>::value ||
1070 : has_member_versioned_save_minimal<T, OutputArchive>::value ||
1071 : has_non_member_versioned_save_minimal<T, OutputArchive>::value) &&
1072 : !(is_specialized_member_serialize<T, OutputArchive>::value ||
1073 : is_specialized_member_save<T, OutputArchive>::value))> {};
1074 :
1075 : // ######################################################################
1076 : // detects if a type has any active minimal input serialization
1077 : template <class T, class InputArchive>
1078 : struct has_minimal_input_serialization : std::integral_constant<bool,
1079 : is_specialized_member_load_minimal<T, InputArchive>::value ||
1080 : ((has_member_load_minimal<T, InputArchive>::value ||
1081 : has_non_member_load_minimal<T, InputArchive>::value ||
1082 : has_member_versioned_load_minimal<T, InputArchive>::value ||
1083 : has_non_member_versioned_load_minimal<T, InputArchive>::value) &&
1084 : !(is_specialized_member_serialize<T, InputArchive>::value ||
1085 : is_specialized_member_load<T, InputArchive>::value))> {};
1086 :
1087 : // ######################################################################
1088 : namespace detail
1089 : {
1090 : //! The number of output serialization functions available
1091 : /*! If specialization is being used, we'll count only those; otherwise we'll count everything */
1092 : template <class T, class OutputArchive>
1093 : struct count_output_serializers : std::integral_constant<int,
1094 : count_specializations<T, OutputArchive>::value ? count_specializations<T, OutputArchive>::value :
1095 : has_member_save<T, OutputArchive>::value +
1096 : has_non_member_save<T, OutputArchive>::value +
1097 : has_member_serialize<T, OutputArchive>::value +
1098 : has_non_member_serialize<T, OutputArchive>::value +
1099 : has_member_save_minimal<T, OutputArchive>::value +
1100 : has_non_member_save_minimal<T, OutputArchive>::value +
1101 : /*-versioned---------------------------------------------------------*/
1102 : has_member_versioned_save<T, OutputArchive>::value +
1103 : has_non_member_versioned_save<T, OutputArchive>::value +
1104 : has_member_versioned_serialize<T, OutputArchive>::value +
1105 : has_non_member_versioned_serialize<T, OutputArchive>::value +
1106 : has_member_versioned_save_minimal<T, OutputArchive>::value +
1107 : has_non_member_versioned_save_minimal<T, OutputArchive>::value> {};
1108 : }
1109 :
1110 : template <class T, class OutputArchive>
1111 : struct is_output_serializable : std::integral_constant<bool,
1112 : detail::count_output_serializers<T, OutputArchive>::value == 1> {};
1113 :
1114 : // ######################################################################
1115 : namespace detail
1116 : {
1117 : //! The number of input serialization functions available
1118 : /*! If specialization is being used, we'll count only those; otherwise we'll count everything */
1119 : template <class T, class InputArchive>
1120 : struct count_input_serializers : std::integral_constant<int,
1121 : count_specializations<T, InputArchive>::value ? count_specializations<T, InputArchive>::value :
1122 : has_member_load<T, InputArchive>::value +
1123 : has_non_member_load<T, InputArchive>::value +
1124 : has_member_serialize<T, InputArchive>::value +
1125 : has_non_member_serialize<T, InputArchive>::value +
1126 : has_member_load_minimal<T, InputArchive>::value +
1127 : has_non_member_load_minimal<T, InputArchive>::value +
1128 : /*-versioned---------------------------------------------------------*/
1129 : has_member_versioned_load<T, InputArchive>::value +
1130 : has_non_member_versioned_load<T, InputArchive>::value +
1131 : has_member_versioned_serialize<T, InputArchive>::value +
1132 : has_non_member_versioned_serialize<T, InputArchive>::value +
1133 : has_member_versioned_load_minimal<T, InputArchive>::value +
1134 : has_non_member_versioned_load_minimal<T, InputArchive>::value> {};
1135 : }
1136 :
1137 : template <class T, class InputArchive>
1138 : struct is_input_serializable : std::integral_constant<bool,
1139 : detail::count_input_serializers<T, InputArchive>::value == 1> {};
1140 :
1141 : // ######################################################################
1142 : // Base Class Support
1143 : namespace detail
1144 : {
1145 : struct base_class_id
1146 : {
1147 : template<class T>
1148 3200 : base_class_id(T const * const t) :
1149 : type(typeid(T)),
1150 : ptr(t),
1151 3200 : hash(std::hash<std::type_index>()(typeid(T)) ^ (std::hash<void const *>()(t) << 1))
1152 3200 : { }
1153 :
1154 1600 : bool operator==(base_class_id const & other) const
1155 1600 : { return (type == other.type) && (ptr == other.ptr); }
1156 :
1157 : std::type_index type;
1158 : void const * ptr;
1159 : size_t hash;
1160 : };
1161 4800 : struct base_class_id_hash { size_t operator()(base_class_id const & id) const { return id.hash; } };
1162 : } // namespace detail
1163 :
1164 : namespace detail
1165 : {
1166 : //! Common base type for base class casting
1167 : struct BaseCastBase {};
1168 :
1169 : template <class>
1170 : struct get_base_class;
1171 :
1172 : template <template<typename> class Cast, class Base>
1173 : struct get_base_class<Cast<Base>>
1174 : {
1175 : using type = Base;
1176 : };
1177 :
1178 : //! Base class cast, behave as the test
1179 : template <class Cast, template<class, class> class Test, class Archive,
1180 : bool IsBaseCast = std::is_base_of<BaseCastBase, Cast>::value>
1181 : struct has_minimal_base_class_serialization_impl : Test<typename get_base_class<Cast>::type, Archive>
1182 : { };
1183 :
1184 : //! Not a base class cast
1185 : template <class Cast, template<class, class> class Test, class Archive>
1186 : struct has_minimal_base_class_serialization_impl<Cast,Test, Archive, false> : std::false_type
1187 : { };
1188 : }
1189 :
1190 : //! Checks to see if the base class used in a cast has a minimal serialization
1191 : /*! @tparam Cast Either base_class or virtual_base_class wrapped type
1192 : @tparam Test A has_minimal test (for either input or output)
1193 : @tparam Archive The archive to use with the test */
1194 : template <class Cast, template<class, class> class Test, class Archive>
1195 : struct has_minimal_base_class_serialization : detail::has_minimal_base_class_serialization_impl<Cast, Test, Archive>
1196 : { };
1197 :
1198 :
1199 : // ######################################################################
1200 : namespace detail
1201 : {
1202 : struct shared_from_this_wrapper
1203 : {
1204 : template <class U>
1205 : static auto (check)( U const & t ) -> decltype( ::cereal::access::shared_from_this(t), std::true_type() );
1206 :
1207 : static auto (check)( ... ) -> decltype( std::false_type() );
1208 :
1209 : template <class U>
1210 : static auto get( U const & t ) -> decltype( t.shared_from_this() );
1211 : };
1212 : }
1213 :
1214 : //! Determine if T or any base class of T has inherited from std::enable_shared_from_this
1215 : template<class T>
1216 : struct has_shared_from_this : decltype((detail::shared_from_this_wrapper::check)(std::declval<T>()))
1217 : { };
1218 :
1219 : //! Get the type of the base class of T which inherited from std::enable_shared_from_this
1220 : template <class T>
1221 : struct get_shared_from_this_base
1222 : {
1223 : private:
1224 : using PtrType = decltype(detail::shared_from_this_wrapper::get(std::declval<T>()));
1225 : public:
1226 : //! The type of the base of T that inherited from std::enable_shared_from_this
1227 : using type = typename std::decay<typename PtrType::element_type>::type;
1228 : };
1229 :
1230 : // ######################################################################
1231 : //! Extracts the true type from something possibly wrapped in a cereal NoConvert
1232 : /*! Internally cereal uses some wrapper classes to test the validity of non-member
1233 : minimal load and save functions. This can interfere with user type traits on
1234 : templated load and save minimal functions. To get to the correct underlying type,
1235 : users should use strip_minimal when performing any enable_if type type trait checks.
1236 :
1237 : See the enum serialization in types/common.hpp for an example of using this */
1238 : template <class T, bool IsCerealMinimalTrait = std::is_base_of<detail::NoConvertBase, T>::value>
1239 : struct strip_minimal
1240 : {
1241 : using type = T;
1242 : };
1243 :
1244 : //! Specialization for types wrapped in a NoConvert
1245 : template <class T>
1246 : struct strip_minimal<T, true>
1247 : {
1248 : using type = typename T::type;
1249 : };
1250 :
1251 : // ######################################################################
1252 : //! Determines whether the class T can be default constructed by cereal::access
1253 : template <class T>
1254 : struct is_default_constructible
1255 : {
1256 : #ifdef CEREAL_OLDER_GCC
1257 : template <class TT, class SFINAE = void>
1258 : struct test : no {};
1259 : template <class TT>
1260 : struct test<TT, typename detail::Void< decltype( cereal::access::construct<TT>() ) >::type> : yes {};
1261 : static const bool value = test<T>();
1262 : #else // NOT CEREAL_OLDER_GCC =========================================
1263 : template <class TT>
1264 : static auto test(int) -> decltype( cereal::access::construct<TT>(), yes());
1265 : template <class>
1266 : static no test(...);
1267 : static const bool value = std::is_same<decltype(test<T>(0)), yes>::value;
1268 : #endif // NOT CEREAL_OLDER_GCC
1269 : };
1270 :
1271 : // ######################################################################
1272 : namespace detail
1273 : {
1274 : //! Removes all qualifiers and minimal wrappers from an archive
1275 : template <class A>
1276 : using decay_archive = typename std::decay<typename strip_minimal<A>::type>::type;
1277 : }
1278 :
1279 : //! Checks if the provided archive type is equal to some cereal archive type
1280 : /*! This automatically does things such as std::decay and removing any other wrappers that may be
1281 : on the Archive template parameter.
1282 :
1283 : Example use:
1284 : @code{cpp}
1285 : // example use to disable a serialization function
1286 : template <class Archive, EnableIf<cereal::traits::is_same_archive<Archive, cereal::BinaryOutputArchive>::value> = sfinae>
1287 : void save( Archive & ar, MyType const & mt );
1288 : @endcode */
1289 : template <class ArchiveT, class CerealArchiveT>
1290 : struct is_same_archive : std::integral_constant<bool,
1291 : std::is_same<detail::decay_archive<ArchiveT>, CerealArchiveT>::value>
1292 : { };
1293 :
1294 : // ######################################################################
1295 : //! A macro to use to restrict which types of archives your function will work for.
1296 : /*! This requires you to have a template class parameter named Archive and replaces the void return
1297 : type for your function.
1298 :
1299 : INTYPE refers to the input archive type you wish to restrict on.
1300 : OUTTYPE refers to the output archive type you wish to restrict on.
1301 :
1302 : For example, if we want to limit a serialize to only work with binary serialization:
1303 :
1304 : @code{.cpp}
1305 : template <class Archive>
1306 : CEREAL_ARCHIVE_RESTRICT(BinaryInputArchive, BinaryOutputArchive)
1307 : serialize( Archive & ar, MyCoolType & m )
1308 : {
1309 : ar & m;
1310 : }
1311 : @endcode
1312 :
1313 : If you need to do more restrictions in your enable_if, you will need to do this by hand.
1314 : */
1315 : #define CEREAL_ARCHIVE_RESTRICT(INTYPE, OUTTYPE) \
1316 : typename std::enable_if<cereal::traits::is_same_archive<Archive, INTYPE>::value || cereal::traits::is_same_archive<Archive, OUTTYPE>::value, void>::type
1317 :
1318 : //! Type traits only struct used to mark an archive as human readable (text based)
1319 : /*! Archives that wish to identify as text based/human readable should inherit from
1320 : this struct */
1321 : struct TextArchive {};
1322 :
1323 : //! Checks if an archive is a text archive (human readable)
1324 : template <class A>
1325 : struct is_text_archive : std::integral_constant<bool,
1326 : std::is_base_of<TextArchive, detail::decay_archive<A>>::value>
1327 : { };
1328 : } // namespace traits
1329 :
1330 : // ######################################################################
1331 : namespace detail
1332 : {
1333 : template <class T, class A,
1334 : bool Member = traits::has_member_load_and_construct<T, A>::value,
1335 : bool MemberVersioned = traits::has_member_versioned_load_and_construct<T, A>::value,
1336 : bool NonMember = traits::has_non_member_load_and_construct<T, A>::value,
1337 : bool NonMemberVersioned = traits::has_non_member_versioned_load_and_construct<T, A>::value>
1338 : struct Construct
1339 : {
1340 : static_assert( cereal::traits::detail::delay_static_assert<T>::value,
1341 : "cereal found more than one compatible load_and_construct function for the provided type and archive combination. \n\n "
1342 : "Types must either have a member load_and_construct function or a non-member specialization of LoadAndConstruct (you may not mix these). \n "
1343 : "In addition, you may not mix versioned with non-versioned load_and_construct functions. \n\n " );
1344 : static T * load_andor_construct( A & /*ar*/, construct<T> & /*construct*/ )
1345 : { return nullptr; }
1346 : };
1347 :
1348 : // no load and construct case
1349 : template <class T, class A>
1350 : struct Construct<T, A, false, false, false, false>
1351 : {
1352 : static_assert( ::cereal::traits::is_default_constructible<T>::value,
1353 : "Trying to serialize a an object with no default constructor. \n\n "
1354 : "Types must either be default constructible or define either a member or non member Construct function. \n "
1355 : "Construct functions generally have the signature: \n\n "
1356 : "template <class Archive> \n "
1357 : "static void load_and_construct(Archive & ar, cereal::construct<T> & construct) \n "
1358 : "{ \n "
1359 : " var a; \n "
1360 : " ar( a ) \n "
1361 : " construct( a ); \n "
1362 : "} \n\n" );
1363 85608 : static T * load_andor_construct()
1364 85608 : { return ::cereal::access::construct<T>(); }
1365 : };
1366 :
1367 : // member non-versioned
1368 : template <class T, class A>
1369 : struct Construct<T, A, true, false, false, false>
1370 : {
1371 5200 : static void load_andor_construct( A & ar, construct<T> & construct )
1372 : {
1373 5200 : access::load_and_construct<T>( ar, construct );
1374 4800 : }
1375 : };
1376 :
1377 : // member versioned
1378 : template <class T, class A>
1379 : struct Construct<T, A, false, true, false, false>
1380 : {
1381 800 : static void load_andor_construct( A & ar, construct<T> & construct )
1382 : {
1383 800 : const auto version = ar.template loadClassVersion<T>();
1384 800 : access::load_and_construct<T>( ar, construct, version );
1385 800 : }
1386 : };
1387 :
1388 : // non-member non-versioned
1389 : template <class T, class A>
1390 : struct Construct<T, A, false, false, true, false>
1391 : {
1392 1604 : static void load_andor_construct( A & ar, construct<T> & construct )
1393 : {
1394 1604 : LoadAndConstruct<T>::load_and_construct( ar, construct );
1395 1604 : }
1396 : };
1397 :
1398 : // non-member versioned
1399 : template <class T, class A>
1400 : struct Construct<T, A, false, false, false, true>
1401 : {
1402 800 : static void load_andor_construct( A & ar, construct<T> & construct )
1403 : {
1404 800 : const auto version = ar.template loadClassVersion<T>();
1405 800 : LoadAndConstruct<T>::load_and_construct( ar, construct, version );
1406 800 : }
1407 : };
1408 : } // namespace detail
1409 : } // namespace cereal
1410 :
1411 : #endif // CEREAL_DETAILS_TRAITS_HPP_
|