1 /* Copyright 2009-2016 Francesco Biscani (bluescarni@gmail.com)
2
3 This file is part of the Piranha library.
4
5 The Piranha library is free software; you can redistribute it and/or modify
6 it under the terms of either:
7
8 * the GNU Lesser General Public License as published by the Free
9 Software Foundation; either version 3 of the License, or (at your
10 option) any later version.
11
12 or
13
14 * the GNU General Public License as published by the Free Software
15 Foundation; either version 3 of the License, or (at your option) any
16 later version.
17
18 or both in parallel, as here.
19
20 The Piranha library is distributed in the hope that it will be useful, but
21 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
22 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
23 for more details.
24
25 You should have received copies of the GNU General Public License and the
26 GNU Lesser General Public License along with the Piranha library. If not,
27 see https://www.gnu.org/licenses/. */
28
29 #ifndef PIRANHA_S11N_HPP
30 #define PIRANHA_S11N_HPP
31
32 // Common headers for serialization.
33 #include <boost/algorithm/string/predicate.hpp>
34 #include <boost/archive/binary_iarchive.hpp>
35 #include <boost/archive/binary_oarchive.hpp>
36 #include <boost/archive/text_iarchive.hpp>
37 #include <boost/archive/text_oarchive.hpp>
38 #include <boost/iostreams/filtering_stream.hpp>
39 #include <boost/mpl/bool.hpp>
40 #include <boost/serialization/split_free.hpp>
41 #include <boost/serialization/split_member.hpp>
42 #include <boost/serialization/string.hpp>
43 #include <boost/version.hpp>
44 #include <cstddef>
45 #include <fstream>
46 #include <ios>
47 #include <memory>
48 #include <stdexcept>
49 #include <string>
50 #include <type_traits>
51 #include <utility>
52
53 #include "detail/demangle.hpp"
54 #include "exceptions.hpp"
55 #include "is_key.hpp"
56 #include "safe_cast.hpp"
57 #include "symbol_set.hpp"
58 #include "type_traits.hpp"
59
60 namespace piranha
61 {
62
63 inline namespace impl
64 {
65
66 // Scalar types directly supported by the all serialization libraries.
67 template <typename T>
68 using is_serialization_scalar = std::
69 integral_constant<bool,
70 disjunction<std::is_same<char, T>, std::is_same<signed char, T>, std::is_same<unsigned char, T>,
71 std::is_same<short, T>, std::is_same<unsigned short, T>, std::is_same<int, T>,
72 std::is_same<unsigned, T>, std::is_same<long, T>, std::is_same<unsigned long, T>,
73 std::is_same<long long, T>, std::is_same<unsigned long long, T>,
74 std::is_same<float, T>, std::is_same<double, T>, std::is_same<bool, T>>::value>;
75
76 // Implementation of the detection of boost saving archives.
77 namespace ibsa_impl
78 {
79
80 template <typename A>
81 using is_saving_t = typename A::is_saving;
82
83 template <typename A>
84 using is_loading_t = typename A::is_loading;
85
86 template <typename A, typename T>
87 using lshift_t = decltype(std::declval<A &>() << std::declval<const T &>());
88
89 template <typename A, typename T>
90 using and_t = decltype(std::declval<A &>() & std::declval<const T &>());
91
92 // NOTE: here it does not make much sense that the pointer is non-const, but we are going by the literal
93 // description in the boost archive concept.
94 template <typename A, typename T>
95 using save_binary_t = decltype(std::declval<A &>().save_binary(std::declval<T *>(), std::declval<std::size_t>()));
96
97 template <typename A, typename T>
98 using register_type_t = decltype(std::declval<A &>().template register_type<T>());
99
100 template <typename A>
101 using get_library_version_t = decltype(std::declval<const A &>().get_library_version());
102
103 struct helper;
104
105 template <typename A>
106 using get_helper_t_1 = decltype(std::declval<A &>().template get_helper<helper>());
107
108 template <typename A>
109 using get_helper_t_2 = decltype(std::declval<A &>().template get_helper<helper>(static_cast<void *>(nullptr)));
110
111 template <typename Archive, typename T>
112 using impl = std::
113 integral_constant<bool,
114 conjunction<std::is_same<detected_t<is_saving_t, uncvref_t<Archive>>, boost::mpl::bool_<true>>,
115 std::is_same<detected_t<is_loading_t, uncvref_t<Archive>>, boost::mpl::bool_<false>>,
116 // NOTE: add lvalue ref instead of using Archive &, so we avoid a hard
117 // error if Archive is void.
118 std::is_same<detected_t<lshift_t, Archive, T>, addlref_t<Archive>>,
119 std::is_same<detected_t<and_t, Archive, T>, addlref_t<Archive>>,
120 is_detected<save_binary_t, Archive, unref_t<T>>,
121 is_detected<register_type_t, Archive, uncvref_t<T>>,
122 // NOTE: the docs here mention that get_library_version() is supposed to
123 // return an unsigned integral type, but the boost archives apparently
124 // return a type which is implicitly convertible to some unsigned int.
125 // This seems to work and it should cover also the cases in which the
126 // return type is a real unsigned int.
127 std::is_convertible<detected_t<get_library_version_t, Archive>, unsigned long long>
128 #if BOOST_VERSION >= 105700
129 // Helper support is available since 1.57.
130 ,
131 is_detected<get_helper_t_1, Archive>, is_detected<get_helper_t_2, Archive>
132 #endif
133 >::value>;
134 }
135 }
136
137 /// Detect Boost saving archives.
138 /**
139 * This type trait will be \p true if \p Archive is a valid Boost saving archive for type \p T,
140 * \p false otherwise. The Boost saving archive concept is described at
141 * http://www.boost.org/doc/libs/1_61_0/libs/serialization/doc/archives.html.
142 */
143 template <typename Archive, typename T>
144 class is_boost_saving_archive
145 {
146 static const bool implementation_defined = ibsa_impl::impl<Archive, T>::value;
147
148 public:
149 /// Value of the type trait.
150 static const bool value = implementation_defined;
151 };
152
153 template <typename Archive, typename T>
154 const bool is_boost_saving_archive<Archive, T>::value;
155
156 inline namespace impl
157 {
158
159 // Implementation of boost loading archive concept. We reuse some types from the ibsa_impl namespace.
160 namespace ibla_impl
161 {
162
163 template <typename A, typename T>
164 using rshift_t = decltype(std::declval<A &>() >> std::declval<T &>());
165
166 template <typename A, typename T>
167 using and_t = decltype(std::declval<A &>() & std::declval<T &>());
168
169 template <typename A, typename T>
170 using load_binary_t = decltype(std::declval<A &>().load_binary(std::declval<T *>(), std::declval<std::size_t>()));
171
172 template <typename A, typename T>
173 using reset_object_address_t
174 = decltype(std::declval<A &>().reset_object_address(std::declval<T *>(), std::declval<T *>()));
175
176 template <typename A>
177 using delete_created_pointers_t = decltype(std::declval<A &>().delete_created_pointers());
178
179 template <typename Archive, typename T>
180 using impl
181 = std::integral_constant<bool,
182 conjunction<std::is_same<detected_t<ibsa_impl::is_saving_t, uncvref_t<Archive>>,
183 boost::mpl::bool_<false>>,
184 std::is_same<detected_t<ibsa_impl::is_loading_t, uncvref_t<Archive>>,
185 boost::mpl::bool_<true>>,
186 std::is_same<detected_t<rshift_t, Archive, T>, addlref_t<Archive>>,
187 std::is_same<detected_t<and_t, Archive, T>, addlref_t<Archive>>,
188 is_detected<load_binary_t, Archive, unref_t<T>>,
189 is_detected<ibsa_impl::register_type_t, Archive, uncvref_t<T>>,
190 std::is_convertible<detected_t<ibsa_impl::get_library_version_t, Archive>,
191 unsigned long long>,
192 is_detected<reset_object_address_t, Archive, unref_t<T>>,
193 is_detected<delete_created_pointers_t, Archive>
194 #if BOOST_VERSION >= 105700
195 ,
196 is_detected<ibsa_impl::get_helper_t_1, Archive>,
197 is_detected<ibsa_impl::get_helper_t_2, Archive>
198 #endif
199 >::value>;
200 }
201 }
202
203 /// Detect Boost loading archives.
204 /**
205 * This type trait will be \p true if \p Archive is a valid Boost loading archive for type \p T,
206 * \p false otherwise. The Boost loading archive concept is described at
207 * http://www.boost.org/doc/libs/1_61_0/libs/serialization/doc/archives.html.
208 */
209 template <typename Archive, typename T>
210 class is_boost_loading_archive
211 {
212 static const bool implementation_defined = ibla_impl::impl<Archive, T>::value;
213
214 public:
215 /// Value of the type trait.
216 static const bool value = implementation_defined;
217 };
218
219 template <typename Archive, typename T>
220 const bool is_boost_loading_archive<Archive, T>::value;
221
222 /// Implementation of piranha::boost_save() via the Boost API.
223 /**
224 * This class can be used to implement piranha::boost_save() via a direct call to the Boost serialization API.
225 */
226 template <typename Archive, typename T>
227 struct boost_save_via_boost_api {
228 /// Call operator.
229 /**
230 * The body of this operator is equivalent to:
231 * @code
232 * ar << x;
233 * @endcode
234 * That is, \p x will be inserted into the archive \p ar using the stream insertion operator. In order for this
235 * method to be callable, the type \p T must provide valid overloads of the methods/functions needed by the
236 * Boost serialization API.
237 *
238 * @param ar the archive into which \p x will be serialized.
239 * @param x the serialization argument.
240 *
241 * @throws unspecified any exception thrown by the insertion of \p x into \p ar.
242 */
operator ()piranha::boost_save_via_boost_api243 void operator()(Archive &ar, const T &x) const
244 {
245 ar << x;
246 }
247 };
248
249 /// Default implementation of piranha::boost_save().
250 /**
251 * The default implementation does not define any call operator, and will thus result
252 * in a compile-time error if used.
253 */
254 template <typename Archive, typename T, typename = void>
255 struct boost_save_impl {
256 };
257
258 inline namespace impl
259 {
260
261 // Enabler for the arithmetic specialisation of boost_save().
262 template <typename Archive, typename T>
263 using boost_save_arithmetic_enabler =
264 // NOTE: the check for non-constness of Archive is implicit in the saving archive concept.
265 // NOTE: here we are relying on the fact that the Archive class correctly advertises the capability
266 // to serialize arithmetic types. This is for instance *not* the case for Boost archives, which
267 // accept every type (at the signature level), but since here we are concerned only with arithmetic
268 // types (for which serialization is always available in Boost archives), it should not matter.
269 enable_if_t<conjunction<is_boost_saving_archive<Archive, T>,
270 disjunction<is_serialization_scalar<T>, std::is_same<T, long double>>>::value>;
271 }
272
273 /// Specialisation of piranha::boost_save() for arithmetic types.
274 /**
275 * \note
276 * This specialisation is enabled if \p Archive and \p T satisfy piranha::is_boost_saving_archive and
277 * \p T is either a floating-point type, or one of
278 * - \p char, <tt>signed char</tt>, or <tt>unsigned char</tt>,
279 * - \p int or <tt>unsigned</tt>,
280 * - \p long or <tt>unsigned long</tt>,
281 * - <tt>long long</tt> or <tt>unsigned long long</tt>,
282 * - \p bool.
283 */
284 template <typename Archive, typename T>
285 struct boost_save_impl<Archive, T, boost_save_arithmetic_enabler<Archive, T>> : boost_save_via_boost_api<Archive, T> {
286 };
287
288 inline namespace impl
289 {
290
291 // Enabler for boost_save() for strings.
292 template <typename Archive>
293 using boost_save_string_enabler = enable_if_t<is_boost_saving_archive<Archive, std::string>::value>;
294 }
295
296 /// Specialisation of piranha::boost_save() for \p std::string.
297 /**
298 * \note
299 * This specialisation is enabled if \p Archive and \p T satisfy piranha::is_boost_saving_archive and \p T is
300 * \p std::string.
301 */
302 template <typename Archive>
303 struct boost_save_impl<Archive, std::string, boost_save_string_enabler<Archive>>
304 : boost_save_via_boost_api<Archive, std::string> {
305 };
306
307 inline namespace impl
308 {
309
310 template <typename Archive, typename T>
311 using boost_save_impl_t = decltype(boost_save_impl<Archive, T>{}(std::declval<Archive &>(), std::declval<const T &>()));
312
313 // Enabler for boost_save().
314 template <typename Archive, typename T>
315 using boost_save_enabler
316 = enable_if_t<conjunction<is_boost_saving_archive<Archive, T>, is_detected<boost_save_impl_t, Archive, T>>::value,
317 int>;
318 }
319
320 /// Save to Boost archive.
321 /**
322 * \note
323 * This function is enabled only if \p Archive and \p T satisfy piranha::is_boost_saving_archive, and the expression
324 * <tt>boost_save_impl<Archive, T>{}(ar, x)</tt> is well-formed.
325 *
326 * This function will save to the Boost archive \p ar the object \p x. The implementation of this function is in
327 * the call operator of the piranha::boost_save_impl functor. The body of this function is equivalent to:
328 * @code
329 * boost_save_impl<Archive, T>{}(ar, x);
330 * @endcode
331 *
332 * @param ar target Boost saving archive.
333 * @param x object to be saved.
334 *
335 * @throws unspecified any exception thrown by the call operator of piranha::boost_save_impl.
336 */
337 template <typename Archive, typename T, boost_save_enabler<Archive, T> = 0>
boost_save(Archive & ar,const T & x)338 inline void boost_save(Archive &ar, const T &x)
339 {
340 boost_save_impl<Archive, T>{}(ar, x);
341 }
342
343 inline namespace impl
344 {
345
346 template <typename A, typename T>
347 using boost_save_t = decltype(piranha::boost_save(std::declval<A &>(), std::declval<const T &>()));
348 }
349
350 /// Detect the presence of piranha::boost_save().
351 /**
352 * This type trait will be \p true if piranha::boost_save() can be called with template arguments \p Archive and
353 * \p T, \p false otherwise.
354 */
355 template <typename Archive, typename T>
356 class has_boost_save
357 {
358 static const bool implementation_defined = is_detected<boost_save_t, Archive, T>::value;
359
360 public:
361 /// Value of the type trait.
362 static const bool value = implementation_defined;
363 };
364
365 template <typename Archive, typename T>
366 const bool has_boost_save<Archive, T>::value;
367
368 /// Implementation of piranha::boost_load() via the Boost API.
369 /**
370 * This class can be used to implement piranha::boost_load() via a direct call to the Boost serialization API.
371 */
372 template <typename Archive, typename T>
373 struct boost_load_via_boost_api {
374 /// Call operator.
375 /**
376 * The body of this operator is equivalent to:
377 * @code
378 * ar >> x;
379 * @endcode
380 * That is, the content of \p ar will loaded into \p x using the stream extraction operator. In order for this
381 * method to be callable, the type \p T must provide valid overloads of the methods/functions needed by the
382 * Boost serialization API.
383 *
384 * @param ar the source archive.
385 * @param x the object into which the content of \p ar will be deserialized.
386 *
387 * @throws unspecified any exception thrown by the extraction of \p ar into \p x.
388 */
operator ()piranha::boost_load_via_boost_api389 void operator()(Archive &ar, T &x) const
390 {
391 ar >> x;
392 }
393 };
394
395 /// Default implementation of piranha::boost_load().
396 /**
397 * The default implementation does not define any call operator, and will thus result
398 * in a compile-time error if used.
399 */
400 template <typename Archive, typename T, typename = void>
401 struct boost_load_impl {
402 };
403
404 inline namespace impl
405 {
406
407 // Enabler for the arithmetic specialisation of boost_load().
408 template <typename Archive, typename T>
409 using boost_load_arithmetic_enabler
410 = enable_if_t<conjunction<is_boost_loading_archive<Archive, T>,
411 disjunction<is_serialization_scalar<T>, std::is_same<T, long double>>>::value>;
412 }
413
414 /// Specialisation of piranha::boost_load() for arithmetic types.
415 /**
416 * \note
417 * This specialisation is enabled if \p Archive and \p T satisfy piranha::is_boost_loading_archive and
418 * \p T is either a floating-point type, or one of
419 * - \p char, <tt>signed char</tt>, or <tt>unsigned char</tt>,
420 * - \p int or <tt>unsigned</tt>,
421 * - \p long or <tt>unsigned long</tt>,
422 * - <tt>long long</tt> or <tt>unsigned long long</tt>,
423 * - \p bool.
424 */
425 template <typename Archive, typename T>
426 struct boost_load_impl<Archive, T, boost_load_arithmetic_enabler<Archive, T>> : boost_load_via_boost_api<Archive, T> {
427 };
428
429 inline namespace impl
430 {
431
432 // Enabler for boost_load for strings.
433 template <typename Archive>
434 using boost_load_string_enabler = enable_if_t<is_boost_loading_archive<Archive, std::string>::value>;
435 }
436
437 /// Specialisation of piranha::boost_load() for \p std::string.
438 /**
439 * \note
440 * This specialisation is enabled if \p Archive and \p T satisfy piranha::is_boost_loading_archive and \p T is
441 * \p std::string.
442 */
443 template <typename Archive>
444 struct boost_load_impl<Archive, std::string, boost_load_string_enabler<Archive>>
445 : boost_load_via_boost_api<Archive, std::string> {
446 };
447
448 inline namespace impl
449 {
450
451 template <typename Archive, typename T>
452 using boost_load_impl_t = decltype(boost_load_impl<Archive, T>{}(std::declval<Archive &>(), std::declval<T &>()));
453
454 // Enabler for boost_load().
455 template <typename Archive, typename T>
456 using boost_load_enabler
457 = enable_if_t<conjunction<is_boost_loading_archive<Archive, T>, is_detected<boost_load_impl_t, Archive, T>>::value,
458 int>;
459 }
460
461 /// Load from Boost archive.
462 /**
463 * \note
464 * This function is enabled only if \p Archive and \p T satisfy piranha::is_boost_loading_archive, and the expression
465 * <tt>boost_load_impl<Archive, T>{}(ar, x)</tt> is well-formed.
466 *
467 * This function will load the object \p x from the Boost archive \p ar. The implementation of this function is in
468 * the call operator of the piranha::boost_load_impl functor. The body of this function is equivalent to:
469 * @code
470 * boost_load_impl<Archive, T>{}(ar, x);
471 * @endcode
472 *
473 * @param ar the source Boost loading archive.
474 * @param x the object that will be loaded from \p ar.
475 *
476 * @throws unspecified any exception thrown by the call operator of piranha::boost_load_impl.
477 */
478 template <typename Archive, typename T, boost_load_enabler<Archive, T> = 0>
boost_load(Archive & ar,T & x)479 inline void boost_load(Archive &ar, T &x)
480 {
481 boost_load_impl<Archive, T>{}(ar, x);
482 }
483
484 inline namespace impl
485 {
486
487 template <typename A, typename T>
488 using boost_load_t = decltype(piranha::boost_load(std::declval<A &>(), std::declval<T &>()));
489 }
490
491 /// Detect the presence of piranha::boost_load().
492 /**
493 * This type trait will be \p true if piranha::boost_load() can be called with template arguments \p Archive and
494 * \p T, \p false otherwise.
495 */
496 template <typename Archive, typename T>
497 class has_boost_load
498 {
499 static const bool implementation_defined = is_detected<boost_load_t, Archive, T>::value;
500
501 public:
502 /// Value of the type trait.
503 static const bool value = implementation_defined;
504 };
505
506 template <typename Archive, typename T>
507 const bool has_boost_load<Archive, T>::value;
508
509 /// Wrapper for the serialization of keys via Boost.
510 /**
511 * This is a simple struct that stores a reference to a key (possibly const-qualified) and a const reference
512 * to a piranha::symbol_set. The Boost serialization routines of piranha::series will save/load keys via this
513 * wrapper rather than trying to save/load keys directly. The reason for this is that keys might
514 * need external information (in the form of the series' piranha::symbol_set) in order for the (de)serialization to be
515 * successful.
516 *
517 * Consequently, rather that implementing specialisations of piranha::boost_save_impl and piranha::boost_load_impl
518 * for key types, specialisations of piranha::boost_save_impl and piranha::boost_load_impl for this wrapper structure
519 * should be provided instead.
520 *
521 * This class requires \p Key to satisfy piranha::is_key, otherwise a compile-time error will be produced.
522 */
523 template <typename Key>
524 struct boost_s11n_key_wrapper {
525 private:
526 PIRANHA_TT_CHECK(is_key, Key);
527
528 public:
529 /// Constructor from key and piranha::symbol_set.
530 /**
531 * @param k the input key.
532 * @param ss the reference piranha::symbol_set.
533 */
boost_s11n_key_wrapperpiranha::boost_s11n_key_wrapper534 explicit boost_s11n_key_wrapper(Key &k, const symbol_set &ss)
535 : m_key_m(std::addressof(k)), m_key_c(m_key_m), m_ss(ss)
536 {
537 }
538 /// Constructor from const key and piranha::symbol_set.
539 /**
540 * @param k the input key.
541 * @param ss the reference piranha::symbol_set.
542 */
boost_s11n_key_wrapperpiranha::boost_s11n_key_wrapper543 explicit boost_s11n_key_wrapper(const Key &k, const symbol_set &ss)
544 : m_key_m(nullptr), m_key_c(std::addressof(k)), m_ss(ss)
545 {
546 }
547 /// Reference to the key.
548 /**
549 * This method will return a mutable reference to the key used as a construction argument. If \p this was
550 * constructed from a const key instance, calling this method will raise an exception.
551 *
552 * @return a mutable reference to the key used as a construction argument.
553 *
554 * @throws std::runtime_error if \p this was constructed from a const key instance.
555 */
keypiranha::boost_s11n_key_wrapper556 Key &key()
557 {
558 if (unlikely(!m_key_m)) {
559 piranha_throw(std::runtime_error, "trying to access the mutable key instance of a boost_s11n_key_wrapper "
560 "that was constructed with a const key");
561 }
562 return *m_key_m;
563 }
564 /// Const reference to the key.
565 /**
566 * @return a const reference to the key used as a construction argument.
567 */
keypiranha::boost_s11n_key_wrapper568 const Key &key() const
569 {
570 return *m_key_c;
571 }
572 /// Const reference to the symbol set.
573 /**
574 * @return a const reference to the piranha::symbol_set used as a construction argument.
575 */
sspiranha::boost_s11n_key_wrapper576 const symbol_set &ss() const
577 {
578 return m_ss;
579 }
580
581 private:
582 Key *m_key_m;
583 const Key *m_key_c;
584 const symbol_set &m_ss;
585 };
586 }
587
588 #include "config.hpp"
589
590 #if defined(PIRANHA_WITH_MSGPACK)
591
592 #include <msgpack.hpp>
593
594 #if MSGPACK_VERSION_MAJOR < 2
595
596 #error Minimum msgpack-c version supported is 2.
597
598 #endif
599
600 #include <algorithm>
601 #include <array>
602 #include <boost/iostreams/device/mapped_file.hpp>
603 #include <cmath>
604 #include <cstdint>
605 #include <ios>
606 #include <iterator>
607 #include <limits>
608 #include <locale>
609 #include <sstream>
610 #include <vector>
611
612 namespace piranha
613 {
614
615 // Fwd decls.
616 template <typename, typename>
617 class has_msgpack_pack;
618
619 template <typename>
620 class has_msgpack_convert;
621
622 inline namespace impl
623 {
624
625 // Wrapper for std stream classes for use in msgpack. The reason for this wrapper is that msgpack expects
626 // streams with a write(const char *, std::size_t) method, but in std streams the second param is std::streamsize
627 // (which is a signed int). Hence we wrap the write method to do the safe conversion from size_t to streamsize.
628 template <typename Stream>
629 class msgpack_stream_wrapper : public Stream
630 {
631 public:
632 // Inherit ctors.
633 using Stream::Stream;
write(const typename Stream::char_type * p,std::size_t count)634 auto write(const typename Stream::char_type *p, std::size_t count)
635 -> decltype(std::declval<Stream &>().write(p, std::streamsize(0)))
636 {
637 return static_cast<Stream *>(this)->write(p, safe_cast<std::streamsize>(count));
638 }
639 };
640
641 template <typename T>
642 using msgpack_stream_write_t
643 = decltype(std::declval<T &>().write(std::declval<const char *>(), std::declval<std::size_t>()));
644 }
645
646 /// Detect msgpack stream.
647 /**
648 * This type trait will be \p true if \p T is a type which can be used as template argument to <tt>msgpack::packer</tt>,
649 * \p false otherwise. Specifically, in order for this trait to be true \p T must be a non-const, non-reference type
650 * with a <tt>write()</tt> method with a signature compatible with
651 * @code
652 * T::write(const char *, std::size_t);
653 * @endcode
654 * The return type of the <tt>write()</tt> method is ignored by the type trait.
655 */
656 template <typename T>
657 class is_msgpack_stream
658 {
659 static const bool implementation_defined
660 = conjunction<is_detected<msgpack_stream_write_t, T>, negation<std::is_reference<T>>,
661 negation<std::is_const<T>>>::value;
662
663 public:
664 /// Value of the type trait.
665 static const bool value = implementation_defined;
666 };
667
668 template <typename T>
669 const bool is_msgpack_stream<T>::value;
670
671 /// Serialization format for msgpack.
672 /**
673 * The serialization of non-primitive objects can often be performed in different ways, with different tradeoffs
674 * between performance, storage requirements and portability. The piranha::mp_integer class, for instance, can be
675 * serialized either via a string representation (slow and with high storage requirements, but portable across
676 * architectures, compilers and operating systems) or as an array of platform-dependent unsigned integrals (fast
677 * and compact, but not portable).
678 *
679 * This enum establishes two strategies for msgpack serialization: a portable format, intended
680 * to be usable across different platforms and suitable for the long-term storage of serialized objects, and a binary
681 * format, intended for use in high-performance scenarios (e.g., as temporary on-disk storage during long
682 * or memory-intensive computations). These formats are used by the Piranha msgpack serialization functions
683 * (piranha::msgpack_pack(), piranha::msgpack_convert(), etc.).
684 */
685 enum class msgpack_format {
686 /// Portable.
687 portable,
688 /// Binary.
689 binary
690 };
691
692 /// Default functor for the implementation of piranha::msgpack_pack().
693 /**
694 * This functor can be specialised via the \p std::enable_if mechanism. Default implementation will not define
695 * the call operator, and will hence result in a compilation error when used.
696 */
697 template <typename Stream, typename T, typename = void>
698 struct msgpack_pack_impl {
699 };
700
701 inline namespace impl
702 {
703
704 template <typename Stream, typename T>
705 using msgpack_scalar_enabler = enable_if_t<conjunction<is_msgpack_stream<Stream>, is_serialization_scalar<T>>::value>;
706 }
707
708 /// Specialisation of piranha::msgpack_pack() for fundamental C++ types supported by msgpack.
709 /**
710 * \note
711 * This specialisation is enabled if \p Stream satisfies piranha::is_msgpack_stream and \p T is one of the
712 * following types:
713 * - \p char, <tt>signed char</tt>, or <tt>unsigned char</tt>,
714 * - \p int or <tt>unsigned</tt>,
715 * - \p long or <tt>unsigned long</tt>,
716 * - <tt>long long</tt> or <tt>unsigned long long</tt>,
717 * - \p float or \p double,
718 * - \p bool.
719 *
720 * The call operator will use directly the <tt>pack()</tt> method of the input msgpack packer.
721 */
722 template <typename Stream, typename T>
723 struct msgpack_pack_impl<Stream, T, msgpack_scalar_enabler<Stream, T>> {
724 /// Call operator.
725 /**
726 * @param packer the target packer.
727 * @param x the object to be packed.
728 *
729 * @throws unspecified any exception thrown by <tt>msgpack::packer::pack()</tt>.
730 */
operator ()piranha::msgpack_pack_impl731 void operator()(msgpack::packer<Stream> &packer, const T &x, msgpack_format) const
732 {
733 packer.pack(x);
734 }
735 };
736
737 inline namespace impl
738 {
739
740 template <typename Stream>
741 using msgpack_ld_enabler
742 = enable_if_t<conjunction<is_msgpack_stream<Stream>, has_msgpack_pack<Stream, std::string>>::value>;
743 }
744
745 /// Specialisation of piranha::msgpack_pack() for <tt>long double</tt>.
746 /**
747 * \note
748 * This specialisation is enabled if:
749 * - \p Stream satisfies piranha::is_msgpack_stream,
750 * - \p std::string satisfies piranha::has_msgpack_pack.
751 */
752 template <typename Stream>
753 struct msgpack_pack_impl<Stream, long double, msgpack_ld_enabler<Stream>> {
754 /// Call operator.
755 /**
756 * If \p f is msgpack_format::binary then the byte representation of \p x is packed into \p packer. Otherwise,
757 * \p x is converted into string format and the resulting string will be packed into \p packer.
758 *
759 * @param packer the target packer.
760 * @param x the object to be packed.
761 * @param f the serialization format.
762 *
763 * @throws unspecified any exception thrown by:
764 * - the public interface of \p msgpack::packer and \p std::ostringstream,
765 * - piranha::msgpack_pack().
766 */
operator ()piranha::msgpack_pack_impl767 void operator()(msgpack::packer<Stream> &packer, const long double &x, msgpack_format f) const
768 {
769 if (f == msgpack_format::binary) {
770 packer.pack_bin(sizeof(long double));
771 packer.pack_bin_body(reinterpret_cast<const char *>(&x), sizeof(long double));
772 } else {
773 if (std::isnan(x)) {
774 if (std::signbit(x)) {
775 msgpack_pack(packer, std::string("-nan"), f);
776 } else {
777 msgpack_pack(packer, std::string("+nan"), f);
778 }
779 } else if (std::isinf(x)) {
780 if (std::signbit(x)) {
781 msgpack_pack(packer, std::string("-inf"), f);
782 } else {
783 msgpack_pack(packer, std::string("+inf"), f);
784 }
785 } else {
786 std::ostringstream oss;
787 // Make sure we are using the C locale.
788 oss.imbue(std::locale::classic());
789 // Use scientific format.
790 oss << std::scientific;
791 // http://stackoverflow.com/questions/554063/how-do-i-print-a-double-value-with-full-precision-using-cout
792 // NOTE: this does not mean that the *exact* value of the long double is printed, just that the
793 // value is recovered exactly if reloaded on the same machine. This is a compromise, as the exact
794 // printing of the value in string form would take up hundreds of digits. On the other hand, there is a
795 // similar situation also for float and double, as there is not guarantee that they conform to IEEE. In
796 // the end it seems the only practical approach is to consider all floating-point types as approximate
797 // values, subject to various platform/architecture vagaries.
798 oss.precision(std::numeric_limits<long double>::max_digits10);
799 oss << x;
800 msgpack_pack(packer, oss.str(), f);
801 }
802 }
803 }
804 };
805
806 inline namespace impl
807 {
808
809 template <typename Stream>
810 using msgpack_string_enabler = enable_if_t<is_msgpack_stream<Stream>::value>;
811 }
812
813 /// Specialisation of piranha::msgpack_pack() for \p std::string.
814 /**
815 * \note
816 * This specialisation is enabled if \p Stream satisfies piranha::is_msgpack_stream.
817 *
818 * The call operator will use directly the <tt>pack()</tt> method of the input msgpack packer.
819 */
820 template <typename Stream>
821 struct msgpack_pack_impl<Stream, std::string, msgpack_string_enabler<Stream>> {
822 /// Call operator.
823 /**
824 * @param packer the target packer.
825 * @param s the string to be packed.
826 *
827 * @throws unspecified any exception thrown by <tt>msgpack::packer::pack()</tt>.
828 */
operator ()piranha::msgpack_pack_impl829 void operator()(msgpack::packer<Stream> &packer, const std::string &s, msgpack_format) const
830 {
831 packer.pack(s);
832 }
833 };
834
835 inline namespace impl
836 {
837
838 template <typename Stream, typename T>
839 using msgpack_pack_impl_t = decltype(msgpack_pack_impl<Stream, T>{}(
840 std::declval<msgpack::packer<Stream> &>(), std::declval<const T &>(), std::declval<msgpack_format>()));
841
842 // Enabler for msgpack_pack.
843 template <typename Stream, typename T>
844 using msgpack_pack_enabler
845 = enable_if_t<conjunction<is_msgpack_stream<Stream>, is_detected<msgpack_pack_impl_t, Stream, T>>::value, int>;
846 }
847
848 /// Pack generic object in a msgpack stream.
849 /**
850 * \note
851 * This function is enabled only if \p Stream satisfies piranha::is_msgpack_stream and if
852 * <tt>msgpack_pack_impl<Stream, T>{}(packer, x, f)</tt> is a valid expression.
853 *
854 * This function is intended to pack the input value \p x into a msgpack \p packer using the format
855 * \p f. The actual implementation of this function is in the piranha::msgpack_pack_impl functor.
856 * The body of this function is equivalent to:
857 * @code
858 * msgpack_pack_impl<Stream, T>{}(packer, x, f);
859 * @endcode
860 *
861 * @param packer the msgpack packer object.
862 * @param x the object to be packed into \p packer.
863 * @param f the serialization format.
864 *
865 * @throws unspecified any exception thrown by the call operator piranha::msgpack_pack_impl.
866 */
867 template <typename Stream, typename T, msgpack_pack_enabler<Stream, T> = 0>
msgpack_pack(msgpack::packer<Stream> & packer,const T & x,msgpack_format f)868 inline void msgpack_pack(msgpack::packer<Stream> &packer, const T &x, msgpack_format f)
869 {
870 msgpack_pack_impl<Stream, T>{}(packer, x, f);
871 }
872
873 /// Default functor for the implementation of piranha::msgpack_convert().
874 /**
875 * This functor can be specialised via the \p std::enable_if mechanism. Default implementation will not define
876 * the call operator, and will hence result in a compilation error when used.
877 */
878 template <typename T, typename = void>
879 struct msgpack_convert_impl {
880 };
881
882 inline namespace impl
883 {
884
885 template <typename T>
886 using msgpack_convert_scalar_enabler = enable_if_t<is_serialization_scalar<T>::value>;
887 }
888
889 /// Specialisation of piranha::msgpack_convert() for fundamental C++ types supported by msgpack.
890 /**
891 * \note
892 * This specialisation is enabled if \p T is one of the following types:
893 * - \p char, <tt>signed char</tt>, or <tt>unsigned char</tt>,
894 * - \p int or <tt>unsigned</tt>,
895 * - \p long or <tt>unsigned long</tt>,
896 * - <tt>long long</tt> or <tt>unsigned long long</tt>,
897 * - \p float or \p double,
898 * - \p bool.
899 *
900 * The call operator will use directly the <tt>convert()</tt> method of the input msgpack object.
901 */
902 template <typename T>
903 struct msgpack_convert_impl<T, msgpack_convert_scalar_enabler<T>> {
904 /// Call operator.
905 /**
906 * @param x the output value.
907 * @param o the object to be converted.
908 *
909 * @throws unspecified any exception thrown by <tt>msgpack::object::convert()</tt>.
910 */
operator ()piranha::msgpack_convert_impl911 void operator()(T &x, const msgpack::object &o, msgpack_format) const
912 {
913 o.convert(x);
914 }
915 };
916
917 /// Specialisation of piranha::msgpack_convert() for \p std::string.
918 /**
919 * The call operator will use directly the <tt>convert()</tt> method of the input msgpack object.
920 */
921 template <>
922 struct msgpack_convert_impl<std::string> {
923 /// Call operator.
924 /**
925 * @param s the output string.
926 * @param o the object to be converted.
927 *
928 * @throws unspecified any exception thrown by the public interface of <tt>msgpack::object</tt>.
929 */
operator ()piranha::msgpack_convert_impl930 void operator()(std::string &s, const msgpack::object &o, msgpack_format) const
931 {
932 o.convert(s);
933 }
934 };
935
936 inline namespace impl
937 {
938
939 template <typename T>
940 using msgpack_convert_impl_t = decltype(msgpack_convert_impl<T>{}(
941 std::declval<T &>(), std::declval<const msgpack::object &>(), std::declval<msgpack_format>()));
942
943 // Enabler for msgpack_convert.
944 template <typename T>
945 using msgpack_convert_enabler
946 = enable_if_t<conjunction<negation<std::is_const<T>>, is_detected<msgpack_convert_impl_t, T>>::value, int>;
947 }
948
949 /// Convert msgpack object.
950 /**
951 * \note
952 * This function is enabled only if \p T is not const and if
953 * <tt>msgpack_convert_impl<T>{}(x, o, f)</tt> is a valid expression.
954 *
955 * This function is intended to convert the msgpack object \p o into an instance of type \p T, and to write
956 * the converted value into \p x. The actual implementation of this function is in the piranha::msgpack_convert_impl
957 * functor. The body of this function is equivalent to:
958 * @code
959 * msgpack_convert_impl<T>{}(x, o, f);
960 * @endcode
961 *
962 * @param x the output value.
963 * @param o the msgpack object that will be converted into \p x.
964 * @param f the serialization format.
965 *
966 * @throws unspecified any exception thrown by the call operator piranha::msgpack_convert_impl.
967 */
968 template <typename T, msgpack_convert_enabler<T> = 0>
msgpack_convert(T & x,const msgpack::object & o,msgpack_format f)969 inline void msgpack_convert(T &x, const msgpack::object &o, msgpack_format f)
970 {
971 msgpack_convert_impl<T>{}(x, o, f);
972 }
973
974 inline namespace impl
975 {
976
977 template <typename T>
978 using msgpack_convert_ld_enabler
979 = enable_if_t<conjunction<std::is_same<T, long double>, has_msgpack_convert<std::string>>::value>;
980 }
981
982 /// Specialisation of piranha::msgpack_convert() for <tt>long double</tt>.
983 /**
984 * \note
985 * This specialisation is enabled if \p T is <tt>long double</tt> and \p std::string satisfies
986 * piranha::has_msgpack_convert.
987 */
988 template <typename T>
989 struct msgpack_convert_impl<T, msgpack_convert_ld_enabler<T>> {
990 /// Call operator.
991 /**
992 * @param x the output value.
993 * @param o the object to be converted.
994 * @param f the serialization format.
995 *
996 * @throws unspecified any exception thrown by the public interface of <tt>msgpack::object</tt> and
997 * <tt>std::istringstream</tt>.
998 * @throws std::invalid_argument if the serialized value is a non-finite value not supported by the implementation,
999 * or, when using the msgpack_format::portable format, the deserialized string does not represent a floating-point
1000 * value.
1001 */
operator ()piranha::msgpack_convert_impl1002 void operator()(long double &x, const msgpack::object &o, msgpack_format f) const
1003 {
1004 using lim = std::numeric_limits<long double>;
1005 if (f == msgpack_format::binary) {
1006 std::array<char, sizeof(long double)> tmp;
1007 o.convert(tmp);
1008 std::copy(tmp.begin(), tmp.end(), reinterpret_cast<char *>(&x));
1009 } else {
1010 PIRANHA_MAYBE_TLS std::string tmp;
1011 msgpack_convert(tmp, o, f);
1012 if (tmp == "+nan") {
1013 if (lim::has_quiet_NaN) {
1014 x = std::copysign(lim::quiet_NaN(), 1.l);
1015 } else {
1016 piranha_throw(std::invalid_argument, "cannot deserialize a NaN if the platform does not support "
1017 "quiet NaNs");
1018 }
1019 } else if (tmp == "-nan") {
1020 if (lim::has_quiet_NaN) {
1021 x = std::copysign(lim::quiet_NaN(), -1.l);
1022 } else {
1023 piranha_throw(std::invalid_argument, "cannot deserialize a NaN if the platform does not support "
1024 "quiet NaNs");
1025 }
1026 } else if (tmp == "+inf") {
1027 if (lim::has_infinity) {
1028 x = lim::infinity();
1029 } else {
1030 piranha_throw(std::invalid_argument, "infinities are not supported by the platform");
1031 }
1032 } else if (tmp == "-inf") {
1033 if (lim::has_infinity) {
1034 x = std::copysign(lim::infinity(), -1.l);
1035 } else {
1036 piranha_throw(std::invalid_argument, "infinities are not supported by the platform");
1037 }
1038 } else {
1039 std::istringstream iss;
1040 iss.imbue(std::locale::classic());
1041 // NOTE: is seems like the std::scientific format flag has an effect on input
1042 // streams as well. See the example here:
1043 // http://en.cppreference.com/w/cpp/io/manip/fixed
1044 iss >> std::scientific;
1045 iss.str(tmp);
1046 iss >> x;
1047 if (unlikely(iss.fail())) {
1048 piranha_throw(std::invalid_argument, "failed to parse the string '" + tmp + "' as a long double");
1049 }
1050 }
1051 }
1052 }
1053 };
1054
1055 inline namespace impl
1056 {
1057
1058 template <typename Stream, typename T>
1059 using msgpack_pack_t = decltype(piranha::msgpack_pack(std::declval<msgpack::packer<Stream> &>(),
1060 std::declval<const T &>(), std::declval<msgpack_format>()));
1061 }
1062
1063 /// Detect the presence of piranha::msgpack_pack().
1064 /**
1065 * This type trait will be \p true if piranha::msgpack_pack() can be called with template arguments
1066 * \p Stream and \p T, \p false otherwise.
1067 */
1068 template <typename Stream, typename T>
1069 class has_msgpack_pack
1070 {
1071 static const bool implementation_defined = is_detected<msgpack_pack_t, Stream, T>::value;
1072
1073 public:
1074 /// Value of the type trait.
1075 static const bool value = implementation_defined;
1076 };
1077
1078 template <typename Stream, typename T>
1079 const bool has_msgpack_pack<Stream, T>::value;
1080
1081 inline namespace impl
1082 {
1083
1084 template <typename T>
1085 using msgpack_convert_t = decltype(piranha::msgpack_convert(
1086 std::declval<T &>(), std::declval<const msgpack::object &>(), std::declval<msgpack_format>()));
1087 }
1088
1089 /// Detect the presence of piranha::msgpack_convert().
1090 /**
1091 * This type trait will be \p true if piranha::msgpack_convert() can be called with template argument \p T.
1092 */
1093 template <typename T>
1094 class has_msgpack_convert
1095 {
1096 static const bool implementation_defined = is_detected<msgpack_convert_t, T>::value;
1097
1098 public:
1099 /// Value of the type trait.
1100 static const bool value = implementation_defined;
1101 };
1102
1103 template <typename T>
1104 const bool has_msgpack_convert<T>::value;
1105
1106 inline namespace impl
1107 {
1108
1109 template <typename Stream, typename Key>
1110 using key_msgpack_pack_t = decltype(std::declval<const Key &>().msgpack_pack(
1111 std::declval<msgpack::packer<Stream> &>(), std::declval<msgpack_format>(), std::declval<const symbol_set &>()));
1112 }
1113
1114 /// Detect the presence of the <tt>%msgpack_pack()</tt> method in keys.
1115 /**
1116 * This type trait will be \p true if \p Stream satisfies piranha::is_msgpack_stream and the \p Key type has
1117 * a method whose signature is compatible with:
1118 * @code
1119 * Key::msgpack_pack(msgpack::packer<Stream> &, msgpack_format, const symbol_set &) const;
1120 * @endcode
1121 * The return type of the method is ignored by this type trait.
1122 *
1123 * If \p Key, after the removal of cv-ref qualifiers, does not satisfy piranha::is_key,
1124 * a compile-time error will be produced.
1125 */
1126 template <typename Stream, typename Key>
1127 class key_has_msgpack_pack
1128 {
1129 PIRANHA_TT_CHECK(is_key, uncvref_t<Key>);
1130 static const bool implementation_defined
1131 = conjunction<is_detected<key_msgpack_pack_t, Stream, Key>, is_msgpack_stream<Stream>>::value;
1132
1133 public:
1134 /// Value of the type trait.
1135 static const bool value = implementation_defined;
1136 };
1137
1138 template <typename Stream, typename Key>
1139 const bool key_has_msgpack_pack<Stream, Key>::value;
1140
1141 inline namespace impl
1142 {
1143
1144 template <typename Key>
1145 using key_msgpack_convert_t = decltype(std::declval<Key &>().msgpack_convert(
1146 std::declval<const msgpack::object &>(), std::declval<msgpack_format>(), std::declval<const symbol_set &>()));
1147 }
1148
1149 /// Detect the presence of the <tt>%msgpack_convert()</tt> method in keys.
1150 /**
1151 * This type trait will be \p true if the \p Key type has a method whose signature is compatible with:
1152 * @code
1153 * Key::msgpack_convert(const msgpack::object &, msgpack_format, const symbol_set &);
1154 * @endcode
1155 * The return type of the method is ignored by this type trait.
1156 *
1157 * If \p Key, after the removal of cv-ref qualifiers, does not satisfy piranha::is_key,
1158 * a compile-time error will be produced.
1159 */
1160 template <typename Key>
1161 class key_has_msgpack_convert
1162 {
1163 PIRANHA_TT_CHECK(is_key, uncvref_t<Key>);
1164 static const bool implementation_defined = is_detected<key_msgpack_convert_t, Key>::value;
1165
1166 public:
1167 /// Value of the type trait.
1168 static const bool value = implementation_defined;
1169 };
1170
1171 template <typename Key>
1172 const bool key_has_msgpack_convert<Key>::value;
1173 }
1174
1175 #endif
1176
1177 #if defined(PIRANHA_WITH_ZLIB)
1178
1179 #include <boost/iostreams/filter/gzip.hpp>
1180 #include <boost/iostreams/filter/zlib.hpp>
1181
1182 #define PIRANHA_ZLIB_CONDITIONAL(expr) expr
1183
1184 #else
1185
1186 #define PIRANHA_ZLIB_CONDITIONAL(expr) piranha_throw(not_implemented_error, "zlib support is not enabled")
1187
1188 #endif
1189
1190 #if defined(PIRANHA_WITH_BZIP2)
1191
1192 #include <boost/iostreams/filter/bzip2.hpp>
1193
1194 #define PIRANHA_BZIP2_CONDITIONAL(expr) expr
1195
1196 #else
1197
1198 #define PIRANHA_BZIP2_CONDITIONAL(expr) piranha_throw(not_implemented_error, "bzip2 support is not enabled")
1199
1200 #endif
1201
1202 namespace piranha
1203 {
1204
1205 /// Data format.
1206 /**
1207 * Data format used by high-level serialization functions such as piranha::save_file() and piranha::load_file().
1208 * The Boost formats are based on the Boost serialization library, while the msgpack formats are based on the msgpack
1209 * serialization format.
1210 *
1211 * The portable variants are intended to be usable across different architectures and
1212 * Piranha versions, whereas the binary variants are non-portable high-performance serialization formats intended
1213 * for temporary storage. That is, saving a binary archive created with Piranha version \p N on architecture \p A
1214 * and then loading it on a different architecture \p B or using a different Piranha version \p M will result in
1215 * undefined behaviour.
1216 */
1217 enum class data_format {
1218 /// Boost binary.
1219 /**
1220 * This format is based on the Boost binary archives.
1221 */
1222 boost_binary,
1223 /// Boost portable.
1224 /**
1225 * This format is based on the Boost text archives.
1226 */
1227 boost_portable,
1228 /// msgpack binary.
1229 /**
1230 * This format will employ internally the msgpack_format::binary format.
1231 */
1232 msgpack_binary,
1233 /// msgpack portable.
1234 /**
1235 * This format will employ internally the msgpack_format::portable format.
1236 */
1237 msgpack_portable
1238 };
1239
1240 /// Compression format.
1241 /**
1242 * Compression formats used by high-level serialization functions such as piranha::save_file() and piranha::load_file().
1243 */
1244 enum class compression {
1245 /// No compression.
1246 none,
1247 /// bzip2 compression.
1248 bzip2,
1249 /// gzip compression.
1250 gzip,
1251 /// zlib compression.
1252 zlib
1253 };
1254
1255 inline namespace impl
1256 {
1257
1258 // NOTE: no need for ifdefs guards here, as the compression-specific stuff is hidden in the CompressionFilter type.
1259 template <typename CompressionFilter, typename T>
save_file_boost_compress_impl(const T & x,std::ofstream & ofile,data_format f)1260 inline void save_file_boost_compress_impl(const T &x, std::ofstream &ofile, data_format f)
1261 {
1262 piranha_assert(f == data_format::boost_binary || f == data_format::boost_portable);
1263 // NOTE: there seem to be 2 choices here: stream or streambuf. The first does some formatting, while the second
1264 // one is "raw" but does not provide the stream interface (which is used, e.g., by msgpack). Since we always
1265 // open files in binary mode (as suggested by the Boost serialization library), this should not matter in the end.
1266 // Some resources:
1267 // http://stackoverflow.com/questions/1753469/how-to-hook-up-boost-serialization-iostreams-to-serialize-gzip-an-object-to
1268 // https://code.google.com/p/cloudobserver/wiki/TutorialsBoostIOstreams
1269 // http://stackoverflow.com/questions/8116541/what-exactly-is-streambuf-how-do-i-use-it
1270 // http://www.boost.org/doc/libs/1_46_1/libs/serialization/doc/special.html
1271 boost::iostreams::filtering_ostream out;
1272 out.push(CompressionFilter{});
1273 out.push(ofile);
1274 if (f == data_format::boost_binary) {
1275 boost::archive::binary_oarchive oa(out);
1276 boost_save(oa, x);
1277 } else {
1278 boost::archive::text_oarchive oa(out);
1279 boost_save(oa, x);
1280 }
1281 }
1282
1283 // NOTE: the implementation is the specular of the above.
1284 template <typename DecompressionFilter, typename T>
load_file_boost_compress_impl(T & x,std::ifstream & ifile,data_format f)1285 inline void load_file_boost_compress_impl(T &x, std::ifstream &ifile, data_format f)
1286 {
1287 piranha_assert(f == data_format::boost_binary || f == data_format::boost_portable);
1288 boost::iostreams::filtering_istream in;
1289 in.push(DecompressionFilter{});
1290 in.push(ifile);
1291 if (f == data_format::boost_binary) {
1292 boost::archive::binary_iarchive ia(in);
1293 boost_load(ia, x);
1294 } else {
1295 boost::archive::text_iarchive ia(in);
1296 boost_load(ia, x);
1297 }
1298 }
1299
1300 // Main save/load functions for Boost format.
1301 template <typename T, enable_if_t<conjunction<has_boost_save<boost::archive::binary_oarchive, T>,
1302 has_boost_save<boost::archive::text_oarchive, T>>::value,
1303 int> = 0>
save_file_boost_impl(const T & x,const std::string & filename,data_format f,compression c)1304 inline void save_file_boost_impl(const T &x, const std::string &filename, data_format f, compression c)
1305 {
1306 namespace bi = boost::iostreams;
1307 // NOTE: always open in binary mode in order to avoid problems with special formatting in streams.
1308 std::ofstream ofile(filename, std::ios::out | std::ios::binary | std::ios::trunc);
1309 if (unlikely(!ofile.good())) {
1310 piranha_throw(std::runtime_error, "file '" + filename + "' could not be opened for saving");
1311 }
1312 switch (c) {
1313 case compression::bzip2:
1314 PIRANHA_BZIP2_CONDITIONAL(save_file_boost_compress_impl<bi::bzip2_compressor>(x, ofile, f));
1315 break;
1316 case compression::gzip:
1317 PIRANHA_ZLIB_CONDITIONAL(save_file_boost_compress_impl<bi::gzip_compressor>(x, ofile, f));
1318 break;
1319 case compression::zlib:
1320 PIRANHA_ZLIB_CONDITIONAL(save_file_boost_compress_impl<bi::zlib_compressor>(x, ofile, f));
1321 break;
1322 case compression::none:
1323 if (f == data_format::boost_binary) {
1324 boost::archive::binary_oarchive oa(ofile);
1325 boost_save(oa, x);
1326 } else {
1327 boost::archive::text_oarchive oa(ofile);
1328 boost_save(oa, x);
1329 }
1330 }
1331 }
1332
1333 template <typename T, enable_if_t<disjunction<negation<has_boost_save<boost::archive::binary_oarchive, T>>,
1334 negation<has_boost_save<boost::archive::text_oarchive, T>>>::value,
1335 int> = 0>
save_file_boost_impl(const T &,const std::string &,data_format,compression)1336 inline void save_file_boost_impl(const T &, const std::string &, data_format, compression)
1337 {
1338 piranha_throw(not_implemented_error,
1339 "type '" + detail::demangle<T>() + "' does not support serialization via Boost");
1340 }
1341
1342 template <typename T, enable_if_t<conjunction<has_boost_load<boost::archive::binary_iarchive, T>,
1343 has_boost_load<boost::archive::text_iarchive, T>>::value,
1344 int> = 0>
load_file_boost_impl(T & x,const std::string & filename,data_format f,compression c)1345 inline void load_file_boost_impl(T &x, const std::string &filename, data_format f, compression c)
1346 {
1347 namespace bi = boost::iostreams;
1348 std::ifstream ifile(filename, std::ios::in | std::ios::binary);
1349 if (unlikely(!ifile.good())) {
1350 piranha_throw(std::runtime_error, "file '" + filename + "' could not be opened for loading");
1351 }
1352 switch (c) {
1353 case compression::bzip2:
1354 PIRANHA_BZIP2_CONDITIONAL(load_file_boost_compress_impl<bi::bzip2_decompressor>(x, ifile, f));
1355 break;
1356 case compression::gzip:
1357 PIRANHA_ZLIB_CONDITIONAL(load_file_boost_compress_impl<bi::gzip_decompressor>(x, ifile, f));
1358 break;
1359 case compression::zlib:
1360 PIRANHA_ZLIB_CONDITIONAL(load_file_boost_compress_impl<bi::zlib_decompressor>(x, ifile, f));
1361 break;
1362 case compression::none:
1363 if (f == data_format::boost_binary) {
1364 boost::archive::binary_iarchive ia(ifile);
1365 boost_load(ia, x);
1366 } else {
1367 boost::archive::text_iarchive ia(ifile);
1368 boost_load(ia, x);
1369 }
1370 }
1371 }
1372
1373 template <typename T, enable_if_t<disjunction<negation<has_boost_load<boost::archive::binary_iarchive, T>>,
1374 negation<has_boost_load<boost::archive::text_iarchive, T>>>::value,
1375 int> = 0>
load_file_boost_impl(T &,const std::string &,data_format,compression)1376 inline void load_file_boost_impl(T &, const std::string &, data_format, compression)
1377 {
1378 piranha_throw(not_implemented_error,
1379 "type '" + detail::demangle<T>() + "' does not support deserialization via Boost");
1380 }
1381
1382 #if defined(PIRANHA_WITH_MSGPACK)
1383
1384 // Compressed load/save for msgpack.
1385 template <typename CompressionFilter, typename T>
save_file_msgpack_compress_impl(const T & x,msgpack_stream_wrapper<std::ofstream> & ofile,msgpack_format mf)1386 inline void save_file_msgpack_compress_impl(const T &x, msgpack_stream_wrapper<std::ofstream> &ofile, msgpack_format mf)
1387 {
1388 msgpack_stream_wrapper<boost::iostreams::filtering_ostream> out;
1389 out.push(CompressionFilter{});
1390 out.push(ofile);
1391 msgpack::packer<decltype(out)> packer(out);
1392 msgpack_pack(packer, x, mf);
1393 }
1394
1395 template <typename DecompressionFilter, typename T>
load_file_msgpack_compress_impl(T & x,const std::string & filename,msgpack_format mf)1396 inline void load_file_msgpack_compress_impl(T &x, const std::string &filename, msgpack_format mf)
1397 {
1398 std::ifstream ifile(filename, std::ios::in | std::ios::binary);
1399 if (unlikely(!ifile.good())) {
1400 piranha_throw(std::runtime_error, "file '" + filename + "' could not be opened for loading");
1401 }
1402 std::vector<char> vchar;
1403 boost::iostreams::filtering_istream in;
1404 in.push(DecompressionFilter{});
1405 in.push(ifile);
1406 std::copy(std::istreambuf_iterator<char>(in), std::istreambuf_iterator<char>(), std::back_inserter(vchar));
1407 auto oh = msgpack::unpack(vchar.data(), safe_cast<std::size_t>(vchar.size()));
1408 msgpack_convert(x, oh.get(), mf);
1409 }
1410
1411 // Main msgpack load/save functions.
1412 template <
1413 typename T,
1414 enable_if_t<conjunction<has_msgpack_pack<msgpack_stream_wrapper<std::ofstream>, T>,
1415 has_msgpack_pack<msgpack_stream_wrapper<boost::iostreams::filtering_ostream>, T>>::value,
1416 int> = 0>
save_file_msgpack_impl(const T & x,const std::string & filename,data_format f,compression c)1417 inline void save_file_msgpack_impl(const T &x, const std::string &filename, data_format f, compression c)
1418 {
1419 namespace bi = boost::iostreams;
1420 const auto mf = (f == data_format::msgpack_binary) ? msgpack_format::binary : msgpack_format::portable;
1421 msgpack_stream_wrapper<std::ofstream> ofile(filename, std::ios::out | std::ios::binary | std::ios::trunc);
1422 if (unlikely(!ofile.good())) {
1423 piranha_throw(std::runtime_error, "file '" + filename + "' could not be opened for saving");
1424 }
1425 switch (c) {
1426 case compression::bzip2:
1427 PIRANHA_BZIP2_CONDITIONAL(save_file_msgpack_compress_impl<bi::bzip2_compressor>(x, ofile, mf));
1428 break;
1429 case compression::gzip:
1430 PIRANHA_ZLIB_CONDITIONAL(save_file_msgpack_compress_impl<bi::gzip_compressor>(x, ofile, mf));
1431 break;
1432 case compression::zlib:
1433 PIRANHA_ZLIB_CONDITIONAL(save_file_msgpack_compress_impl<bi::zlib_compressor>(x, ofile, mf));
1434 break;
1435 case compression::none: {
1436 msgpack::packer<decltype(ofile)> packer(ofile);
1437 msgpack_pack(packer, x, mf);
1438 }
1439 }
1440 }
1441
1442 template <typename T,
1443 enable_if_t<disjunction<negation<has_msgpack_pack<msgpack_stream_wrapper<std::ofstream>, T>>,
1444 negation<has_msgpack_pack<msgpack_stream_wrapper<boost::iostreams::filtering_ostream>,
1445 T>>>::value,
1446 int> = 0>
save_file_msgpack_impl(const T &,const std::string &,data_format,compression)1447 inline void save_file_msgpack_impl(const T &, const std::string &, data_format, compression)
1448 {
1449 piranha_throw(not_implemented_error,
1450 "type '" + detail::demangle<T>() + "' does not support serialization via msgpack");
1451 }
1452
1453 template <typename T, enable_if_t<has_msgpack_convert<T>::value, int> = 0>
load_file_msgpack_impl(T & x,const std::string & filename,data_format f,compression c)1454 inline void load_file_msgpack_impl(T &x, const std::string &filename, data_format f, compression c)
1455 {
1456 namespace bi = boost::iostreams;
1457 const auto mf = (f == data_format::msgpack_binary) ? msgpack_format::binary : msgpack_format::portable;
1458 switch (c) {
1459 case compression::bzip2:
1460 PIRANHA_BZIP2_CONDITIONAL(load_file_msgpack_compress_impl<bi::bzip2_decompressor>(x, filename, mf));
1461 break;
1462 case compression::gzip:
1463 PIRANHA_ZLIB_CONDITIONAL(load_file_msgpack_compress_impl<bi::gzip_decompressor>(x, filename, mf));
1464 break;
1465 case compression::zlib:
1466 PIRANHA_ZLIB_CONDITIONAL(load_file_msgpack_compress_impl<bi::zlib_decompressor>(x, filename, mf));
1467 break;
1468 case compression::none: {
1469 // NOTE: two-stage construction for exception handling.
1470 std::unique_ptr<bi::mapped_file> mmap;
1471 try {
1472 mmap.reset(new bi::mapped_file(filename, bi::mapped_file::readonly));
1473 } catch (...) {
1474 // NOTE: this is just to beautify a bit the error message, and we assume any error in the line
1475 // above results from being unable to open the file.
1476 piranha_throw(std::runtime_error, "file '" + filename + "' could not be opened for loading");
1477 }
1478 // NOTE: this might be superfluous, but better safe than sorry.
1479 if (unlikely(!mmap->is_open())) {
1480 piranha_throw(std::runtime_error, "file '" + filename + "' could not be opened for loading");
1481 }
1482 auto oh = msgpack::unpack(mmap->const_data(), safe_cast<std::size_t>(mmap->size()));
1483 msgpack_convert(x, oh.get(), mf);
1484 }
1485 }
1486 }
1487
1488 template <typename T, enable_if_t<!has_msgpack_convert<T>::value, int> = 0>
load_file_msgpack_impl(T &,const std::string &,data_format,compression)1489 inline void load_file_msgpack_impl(T &, const std::string &, data_format, compression)
1490 {
1491 piranha_throw(not_implemented_error,
1492 "type '" + detail::demangle<T>() + "' does not support deserialization via msgpack");
1493 }
1494
1495 #else
1496
1497 // If msgpack is not available, just error out.
1498 template <typename T>
save_file_msgpack_impl(const T &,const std::string &,data_format,compression)1499 inline void save_file_msgpack_impl(const T &, const std::string &, data_format, compression)
1500 {
1501 piranha_throw(not_implemented_error, "msgpack support is not enabled");
1502 }
1503
1504 template <typename T>
load_file_msgpack_impl(T &,const std::string &,data_format,compression)1505 inline void load_file_msgpack_impl(T &, const std::string &, data_format, compression)
1506 {
1507 piranha_throw(not_implemented_error, "msgpack support is not enabled");
1508 }
1509
1510 #endif
1511
1512 // General enabler for load_file().
1513 template <typename T>
1514 using load_file_enabler = enable_if_t<!std::is_const<T>::value, int>;
1515
1516 // Utility to deduce compression and data format from a filename.
get_cdf_from_filename(std::string filename)1517 inline std::pair<compression, data_format> get_cdf_from_filename(std::string filename)
1518 {
1519 const auto orig_fname = filename;
1520 compression c = compression::none;
1521 if (boost::ends_with(filename, ".bz2")) {
1522 c = compression::bzip2;
1523 filename.erase(filename.end() - 4, filename.end());
1524 } else if (boost::ends_with(filename, ".gz")) {
1525 c = compression::gzip;
1526 filename.erase(filename.end() - 3, filename.end());
1527 } else if (boost::ends_with(filename, ".zip")) {
1528 c = compression::zlib;
1529 filename.erase(filename.end() - 4, filename.end());
1530 }
1531 data_format f;
1532 if (boost::ends_with(filename, ".boostb")) {
1533 f = data_format::boost_binary;
1534 } else if (boost::ends_with(filename, ".boostp")) {
1535 f = data_format::boost_portable;
1536 } else if (boost::ends_with(filename, ".mpackb")) {
1537 f = data_format::msgpack_binary;
1538 } else if (boost::ends_with(filename, ".mpackp")) {
1539 f = data_format::msgpack_portable;
1540 } else {
1541 piranha_throw(std::invalid_argument,
1542 "unable to deduce the data format from the filename '" + orig_fname
1543 + "'. The filename must end with one of ['.boostb','.boostp','.mpackb','.mpackp'], "
1544 "optionally followed by one of ['.bz2','gz','zip'].");
1545 }
1546 return std::make_pair(c, f);
1547 }
1548 }
1549
1550 /// Save to file.
1551 /**
1552 * This function will save the generic object \p x to the file named \p filename, using the data format
1553 * \p f and the compression method \p c.
1554 *
1555 * This function is built on lower-level routines such as piranha::boost_save() and piranha::msgpack_pack(). The data
1556 * format \p f establishes both the lower level serialization method to be used and its variant (e.g., portable
1557 * vs binary). If requested (i.e., if \p c is not piranha::compression::none), the output file will be compressed.
1558 *
1559 * @param x object to be saved to file.
1560 * @param filename name of the output file.
1561 * @param f data format.
1562 * @param c compression format.
1563 *
1564 * @throws piranha::not_implemented_error in the following cases:
1565 * - the type \p T does not implement the required serialization method (e.g., \p f is
1566 * piranha::data_format::boost_binary but \p T does not provide an implementation of piranha::boost_save()),
1567 * - a necessary optional third-party library (e.g., msgpack or one of the compression libraries)
1568 * is not available on the host platform.
1569 * @throws std::runtime_error in case the file cannot be opened for writing.
1570 * @throws unspecified any exception thrown by:
1571 * - piranha::safe_cast(),
1572 * - the invoked low-level serialization function,
1573 * - the public interface of the Boost iostreams library.
1574 */
1575 template <typename T>
save_file(const T & x,const std::string & filename,data_format f,compression c)1576 inline void save_file(const T &x, const std::string &filename, data_format f, compression c)
1577 {
1578 if (f == data_format::boost_binary || f == data_format::boost_portable) {
1579 save_file_boost_impl(x, filename, f, c);
1580 } else if (f == data_format::msgpack_binary || f == data_format::msgpack_portable) {
1581 save_file_msgpack_impl(x, filename, f, c);
1582 }
1583 }
1584
1585 /// Save to file.
1586 /**
1587 * This is a convenience function that will invoke the other overload of piranha::save_file() trying to guess
1588 * the data and compression formats from the filename. The heuristic is as follows:
1589 * - if \p filename ends in one of the suffixes <tt>.bz2</tt>, <tt>.gz</tt> or <tt>.zip</tt> then the suffix is removed
1590 * for further considerations from \p filename, and the corresponding
1591 * piranha::compression format is assumed (respectively, piranha::compression::bzip2, piranha::compression::gzip
1592 * and piranha::compression::zlib). Otherwise, piranha::compression::none is assumed;
1593 * - after the removal of any compression suffix, the extension of \p filename is examined again: if the extension is
1594 * one of <tt>.boostp</tt>, <tt>.boostb</tt>, <tt>.mpackp</tt> and <tt>.mpackb</tt>, then the corresponding data
1595 * format is selected (respectively, piranha::data_format::boost_portable, piranha::data_format::boost_binary,
1596 * piranha::data_format::msgpack_portable, piranha::data_format::msgpack_binary). Othwewise, an error will be
1597 * produced.
1598 *
1599 * Examples:
1600 * - <tt>foo.boostb.bz2</tt> deduces piranha::data_format::boost_binary and piranha::compression::bzip2;
1601 * - <tt>foo.mpackp</tt> deduces piranha::data_format::msgpack_portable and piranha::compression::none;
1602 * - <tt>foo.txt</tt> produces an error;
1603 * - <tt>foo.bz2</tt> produces an error.
1604 *
1605 * @param x the object that will be saved to file.
1606 * @param filename the desired file name.
1607 *
1608 * @throws std::invalid_argument if the compression and data formats cannot be deduced.
1609 * @throws unspecified any exception throw by the first overload of piranha::save_file().
1610 */
1611 template <typename T>
save_file(const T & x,const std::string & filename)1612 inline void save_file(const T &x, const std::string &filename)
1613 {
1614 const auto p = get_cdf_from_filename(filename);
1615 save_file(x, filename, p.second, p.first);
1616 }
1617
1618 /// Load from file.
1619 /**
1620 * \note
1621 * This function is enabled only if \p T is not const.
1622 *
1623 * This function will load the content of the file named \p filename into the object \p x, assuming that the data is
1624 * stored in the format \p f using the compression method \p c. If \p c is not piranha::compression::none, it will
1625 * be assumed that the file is compressed.
1626 *
1627 * This function is built on lower-level routines such as piranha::boost_load() and piranha::msgpack_convert(). The data
1628 * format \p f establishes both the lower level serialization method to be used and its variant (e.g., portable
1629 * vs binary).
1630 *
1631 * @param x the object into which the content of the file name \p filename will be deserialized.
1632 * @param filename name of the input file.
1633 * @param f data format.
1634 * @param c compression format.
1635 *
1636 * @throws piranha::not_implemented_error in the following cases:
1637 * - the type \p T does not implement the required serialization method (e.g., \p f is
1638 * piranha::data_format::boost_binary but \p T does not provide an implementation of piranha::boost_load()),
1639 * - a necessary optional third-party library (e.g., msgpack or one of the compression libraries)
1640 * is not available on the host platform.
1641 * @throws std::runtime_error in case the file cannot be opened for reading.
1642 * @throws unspecified any exception thrown by:
1643 * - piranha::safe_cast(),
1644 * - the invoked low-level serialization function,
1645 * - the \p new operator,
1646 * - the public interface of the Boost iostreams library.
1647 */
1648 template <typename T, load_file_enabler<T> = 0>
load_file(T & x,const std::string & filename,data_format f,compression c)1649 inline void load_file(T &x, const std::string &filename, data_format f, compression c)
1650 {
1651 if (f == data_format::boost_binary || f == data_format::boost_portable) {
1652 load_file_boost_impl(x, filename, f, c);
1653 } else if (f == data_format::msgpack_binary || f == data_format::msgpack_portable) {
1654 load_file_msgpack_impl(x, filename, f, c);
1655 }
1656 }
1657
1658 /// Load from file .
1659 /**
1660 * \note
1661 * This function is enabled only if \p T is not const.
1662 *
1663 * This is a convenience function that will invoke the other overload of piranha::load_file() trying to guess
1664 * the data and compression formats from the filename. The heuristic is described in the second overload of
1665 * piranha::save_file().
1666 *
1667 * @param x the object into which the file content will be loaded.
1668 * @param filename source file name.
1669 *
1670 * @throws std::invalid_argument if the compression and data formats cannot be deduced.
1671 * @throws unspecified any exception throw by the first overload of piranha::load_file().
1672 */
1673 template <typename T, load_file_enabler<T> = 0>
load_file(T & x,const std::string & filename)1674 inline void load_file(T &x, const std::string &filename)
1675 {
1676 const auto p = get_cdf_from_filename(filename);
1677 load_file(x, filename, p.second, p.first);
1678 }
1679
1680 inline namespace impl
1681 {
1682
1683 #if defined(PIRANHA_WITH_MSGPACK)
1684
1685 // These typedefs are useful when checking the availability of boost save/load member functions, which
1686 // we use fairly often to implement the _impl functors.
1687 template <typename Stream, typename T>
1688 using msgpack_pack_member_t = decltype(
1689 std::declval<const T &>().msgpack_pack(std::declval<msgpack::packer<Stream> &>(), std::declval<msgpack_format>()));
1690
1691 template <typename T>
1692 using msgpack_convert_member_t = decltype(
1693 std::declval<T &>().msgpack_convert(std::declval<const msgpack::object &>(), std::declval<msgpack_format>()));
1694
1695 #endif
1696
1697 // Utility functions to serialize ranges and vector-like types.
1698 template <typename Archive, typename It>
boost_save_range(Archive & ar,It begin,It end)1699 inline void boost_save_range(Archive &ar, It begin, It end)
1700 {
1701 for (; begin != end; ++begin) {
1702 boost_save(ar, *begin);
1703 }
1704 }
1705
1706 template <typename Archive, typename V>
boost_save_vector(Archive & ar,const V & v)1707 inline void boost_save_vector(Archive &ar, const V &v)
1708 {
1709 boost_save(ar, v.size());
1710 boost_save_range(ar, v.begin(), v.end());
1711 }
1712
1713 template <typename Archive, typename It>
boost_load_range(Archive & ar,It begin,It end)1714 inline void boost_load_range(Archive &ar, It begin, It end)
1715 {
1716 for (; begin != end; ++begin) {
1717 boost_load(ar, *begin);
1718 }
1719 }
1720
1721 template <typename Archive, typename V>
boost_load_vector(Archive & ar,V & v)1722 inline void boost_load_vector(Archive &ar, V &v)
1723 {
1724 typename V::size_type size;
1725 boost_load(ar, size);
1726 v.resize(size);
1727 boost_load_range(ar, v.begin(), v.end());
1728 }
1729
1730 // Introduce also enablers to detect when we can use the vector save/load functions.
1731 template <typename Archive, typename V, typename T = void>
1732 using boost_save_vector_enabler = enable_if_t<conjunction<has_boost_save<Archive, typename V::value_type>,
1733 has_boost_save<Archive, typename V::size_type>>::value,
1734 T>;
1735
1736 template <typename Archive, typename V, typename T = void>
1737 using boost_load_vector_enabler = enable_if_t<conjunction<has_boost_load<Archive, typename V::value_type>,
1738 has_boost_load<Archive, typename V::size_type>>::value,
1739 T>;
1740
1741 #if defined(PIRANHA_WITH_MSGPACK)
1742
1743 template <typename Stream, typename It, typename Size>
msgpack_pack_range(msgpack::packer<Stream> & p,It begin,It end,Size s,msgpack_format f)1744 inline void msgpack_pack_range(msgpack::packer<Stream> &p, It begin, It end, Size s, msgpack_format f)
1745 {
1746 p.pack_array(safe_cast<std::uint32_t>(s));
1747 for (; begin != end; ++begin) {
1748 msgpack_pack(p, *begin, f);
1749 }
1750 }
1751
1752 template <typename Stream, typename V>
msgpack_pack_vector(msgpack::packer<Stream> & p,const V & v,msgpack_format f)1753 inline void msgpack_pack_vector(msgpack::packer<Stream> &p, const V &v, msgpack_format f)
1754 {
1755 msgpack_pack_range(p, v.begin(), v.end(), v.size(), f);
1756 }
1757
1758 // Convert the msgpack array in o to a vector of type V.
1759 template <typename V>
msgpack_convert_array(const msgpack::object & o,V & v,msgpack_format f)1760 inline void msgpack_convert_array(const msgpack::object &o, V &v, msgpack_format f)
1761 {
1762 // First extract a vector of objects from o.
1763 PIRANHA_MAYBE_TLS std::vector<msgpack::object> tmp_obj;
1764 o.convert(tmp_obj);
1765 v.resize(safe_cast<decltype(v.size())>(tmp_obj.size()));
1766 for (decltype(v.size()) i = 0; i < v.size(); ++i) {
1767 piranha::msgpack_convert(v[i], tmp_obj[static_cast<decltype(v.size())>(i)], f);
1768 }
1769 }
1770
1771 template <typename Stream, typename V, typename T = void>
1772 using msgpack_pack_vector_enabler
1773 = enable_if_t<conjunction<is_msgpack_stream<Stream>, has_msgpack_pack<Stream, typename V::value_type>,
1774 has_safe_cast<std::uint32_t, typename V::size_type>>::value,
1775 T>;
1776
1777 template <typename V, typename T = void>
1778 using msgpack_convert_array_enabler
1779 = enable_if_t<conjunction<has_safe_cast<typename V::size_type, typename std::vector<msgpack::object>::size_type>,
1780 has_msgpack_convert<typename V::value_type>>::value,
1781 T>;
1782
1783 #endif
1784 }
1785 }
1786
1787 #undef PIRANHA_ZLIB_CONDITIONAL
1788
1789 #endif
1790