From 20c457c2dd7d52433f2235577800acac1c39fe65 Mon Sep 17 00:00:00 2001 From: Christian Pfligersdorffer Date: Tue, 16 Feb 2016 00:20:23 +0100 Subject: [PATCH 1/5] Added dependency to program_options --- test/Jamfile.v2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index a42bced472..4ab4ba1684 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -8,7 +8,7 @@ project libs/serialization/test : id serialization_test - : requirements /boost/filesystem + : requirements /boost/filesystem /boost/program_options ; # import rules for testing conditional on config file variables From 4462acbddd39f8c82da7d75ca508484e14c8b290 Mon Sep 17 00:00:00 2001 From: Christian Pfligersdorffer Date: Sat, 20 Feb 2016 22:38:10 +0100 Subject: [PATCH 2/5] Use boost::endian library for endian conversion instead of deprecated boost::spirit::detail::endian --- include/boost/archive/portable_archive.hpp | 35 --------------------- include/boost/archive/portable_iarchive.hpp | 15 ++++++--- include/boost/archive/portable_oarchive.hpp | 15 ++++++--- 3 files changed, 22 insertions(+), 43 deletions(-) delete mode 100755 include/boost/archive/portable_archive.hpp diff --git a/include/boost/archive/portable_archive.hpp b/include/boost/archive/portable_archive.hpp deleted file mode 100755 index ec8149cc3b..0000000000 --- a/include/boost/archive/portable_archive.hpp +++ /dev/null @@ -1,35 +0,0 @@ -/*****************************************************************************/ -/** - * \file portable_archive.hpp - * \brief Needed for unit tests on portable archives. - * \author christian.pfligersdorffer@gmx.at - * - * Header for testing portable archives with all of the serialization tests. - * Before use copy all hpp files from this directory to your boost folder - * boost_.../libs/serialization/test and run from there a visual studio - * prompt with b2 oder bjam -sBOOST_ARCHIVE_LIST=portable_archive.hpp - * - * \note Since portable archives version 5.0 we depend on program_options! - * Edit libs/serialization/test/Jamfile.v2 and change the requirements to - * : requirements /boost/filesystem /boost/program_options - */ -/****************************************************************************/ - -#pragma warning( disable:4217 4127 4310 4244 4800 4267 ) - -// portable_archive test header -// include output archive header -#include "portable_oarchive.hpp" -// set name of test output archive -typedef eos::portable_oarchive test_oarchive; -// set name of test output stream -typedef std::ofstream test_ostream; - -// repeat the above for input archive -#include "portable_iarchive.hpp" -typedef eos::portable_iarchive test_iarchive; -typedef std::ifstream test_istream; - -// define open mode for streams -// binary archives should use std::ios_base::binary -#define TEST_STREAM_FLAGS std::ios_base::binary diff --git a/include/boost/archive/portable_iarchive.hpp b/include/boost/archive/portable_iarchive.hpp index 67763f222c..b1482730a0 100755 --- a/include/boost/archive/portable_iarchive.hpp +++ b/include/boost/archive/portable_iarchive.hpp @@ -3,7 +3,7 @@ * \file portable_iarchive.hpp * \brief Provides an archive to read from portable binary files. * \author christian.pfligersdorffer@gmx.at - * \version 5.1 + * \version 5.0 * * This pair of archives brings the advantages of binary streams to the cross * platform boost::serialization user. While being almost as fast as the native @@ -117,9 +117,12 @@ #elif BOOST_VERSION < 104800 #include #include -#else +#elif BOOST_VERSION < 105800 #include #include +#else +#include +#include #endif // namespace alias @@ -132,7 +135,7 @@ namespace fp = boost::spirit::math; // namespace alias endian #if BOOST_VERSION < 104800 namespace endian = boost::detail; -#else +#elif BOOST_VERSION < 105800 namespace endian = boost::spirit::detail; #endif @@ -347,10 +350,14 @@ namespace eos { // reconstruct the value T temp = size < 0 ? -1 : 0; load_binary(&temp, abs(size)); - + #if BOOST_VERSION < 105800 // load the value from little endian - it is then converted // to the target type T and fits it because size <= sizeof(T) t = endian::load_little_endian(&temp); + #else + // use new endian library from boost 1.58 + t = boost::endian::little_to_native(temp); + #endif } else t = 0; // zero optimization diff --git a/include/boost/archive/portable_oarchive.hpp b/include/boost/archive/portable_oarchive.hpp index 100176e9e5..2e26100a6a 100755 --- a/include/boost/archive/portable_oarchive.hpp +++ b/include/boost/archive/portable_oarchive.hpp @@ -120,12 +120,15 @@ #elif BOOST_VERSION < 104800 #include #include -#else +#elif BOOST_VERSION < 105800 #include #include +#else +#include +#include #endif -// namespace alias fp_classify +// namespace alias #if BOOST_VERSION < 103800 namespace fp = boost::math; #else @@ -135,7 +138,7 @@ namespace fp = boost::spirit::math; // namespace alias endian #if BOOST_VERSION < 104800 namespace endian = boost::detail; -#else +#elif BOOST_VERSION < 105800 namespace endian = boost::spirit::detail; #endif @@ -325,10 +328,14 @@ namespace eos { // encode the sign bit into the size save_signed_char(t > 0 ? size : -size); BOOST_ASSERT(t > 0 || boost::is_signed::value); - + #if BOOST_VERSION < 105800 // we choose to use little endian because this way we just // save the first size bytes to the stream and skip the rest endian::store_little_endian(&temp, t); + #else + // use new endian library from boost 1.58 + temp = boost::endian::native_to_little(t); + #endif save_binary(&temp, size); } From f194f42af7104f1608a1f27c6a3c304d4ac1f8e6 Mon Sep 17 00:00:00 2001 From: Christian Pfligersdorffer Date: Sat, 20 Feb 2016 23:11:29 +0100 Subject: [PATCH 3/5] Bring portable archives into boost::archive namespace --- .../archive/portable_archive_exception.hpp | 128 +++-- include/boost/archive/portable_iarchive.hpp | 536 +++++++++--------- include/boost/archive/portable_oarchive.hpp | 495 ++++++++-------- test/polymorphic_portable_archive.hpp | 4 +- test/portable_archive.hpp | 4 +- 5 files changed, 586 insertions(+), 581 deletions(-) diff --git a/include/boost/archive/portable_archive_exception.hpp b/include/boost/archive/portable_archive_exception.hpp index 719fc00440..a95dde0901 100755 --- a/include/boost/archive/portable_archive_exception.hpp +++ b/include/boost/archive/portable_archive_exception.hpp @@ -18,76 +18,78 @@ #include #include -namespace eos { +namespace boost { + namespace archive { - // this value is written to the top of the stream - const signed char magic_byte = 'e' | 'o' | 's'; + // this value is written to the top of the stream + const signed char magic_byte = 'e' | 'o' | 's'; - // flag for fp serialization - const unsigned no_infnan = 64; + // flag for fp serialization + const unsigned no_infnan = 64; - // integral type for the archive version - #if BOOST_VERSION < 104400 - typedef boost::archive::version_type archive_version_type; - #else - typedef boost::archive::library_version_type archive_version_type; - #endif + // integral type for the archive version +#if BOOST_VERSION < 104400 + typedef version_type archive_version_type; +#else + typedef library_version_type archive_version_type; +#endif - // version of the linked boost archive library - const archive_version_type archive_version( - #if BOOST_VERSION < 103700 - boost::archive::ARCHIVE_VERSION() - #else - boost::archive::BOOST_ARCHIVE_VERSION() - #endif - ); + // version of the linked boost archive library + const archive_version_type archive_version( +#if BOOST_VERSION < 103700 + ARCHIVE_VERSION() +#else + BOOST_ARCHIVE_VERSION() +#endif + ); - /** - * \brief Exception being thrown when serialization cannot proceed. - * - * There are several situations in which the portable archives may fail and - * hence throw an exception: - * -# deserialization of an integer value that exceeds the range of the type - * -# (de)serialization of inf/nan through an archive with no_infnan flag set - * -# deserialization of a denormalized value without the floating point type - * supporting denormalized numbers - * - * Note that this exception will also be thrown if you mixed up your stream - * position and accidentially interpret some value for size data (in this case - * the reported size will be totally amiss most of the time). - */ - class portable_archive_exception : public boost::archive::archive_exception - { - std::string msg; - - public: - //! type size is not large enough for deserialized number - portable_archive_exception(signed char invalid_size) - : boost::archive::archive_exception(other_exception) - , msg("requested integer size exceeds type size: ") + /** + * \brief Exception being thrown when serialization cannot proceed. + * + * There are several situations in which the portable archives may fail and + * hence throw an exception: + * -# deserialization of an integer value that exceeds the range of the type + * -# (de)serialization of inf/nan through an archive with no_infnan flag set + * -# deserialization of a denormalized value without the floating point type + * supporting denormalized numbers + * + * Note that this exception will also be thrown if you mixed up your stream + * position and accidentially interpret some value for size data (in this case + * the reported size will be totally amiss most of the time). + */ + class portable_archive_exception : public archive_exception { - msg += boost::lexical_cast(invalid_size); - } + std::string msg; - //! negative number in unsigned type - portable_archive_exception() - : boost::archive::archive_exception(other_exception) - , msg("cannot read a negative number into an unsigned type") - { - } + public: + //! type size is not large enough for deserialized number + portable_archive_exception(signed char invalid_size) + : archive_exception(other_exception) + , msg("requested integer size exceeds type size: ") + { + msg += lexical_cast(invalid_size); + } - //! serialization of inf, nan and denormals - template - portable_archive_exception(const T& abnormal) - : boost::archive::archive_exception(other_exception) - , msg("serialization of illegal floating point value: ") - { - msg += boost::lexical_cast(abnormal); - } + //! negative number in unsigned type + portable_archive_exception() + : archive_exception(other_exception) + , msg("cannot read a negative number into an unsigned type") + { + } + + //! serialization of inf, nan and denormals + template + portable_archive_exception(const T& abnormal) + : archive_exception(other_exception) + , msg("serialization of illegal floating point value: ") + { + msg += lexical_cast(abnormal); + } - //! override the base class function with our message - const char* what() const throw() { return msg.c_str(); } - ~portable_archive_exception() throw() {} - }; + //! override the base class function with our message + const char* what() const throw() { return msg.c_str(); } + ~portable_archive_exception() throw() {} + }; -} // namespace eos + } // namespace archive +} // namespace boost diff --git a/include/boost/archive/portable_iarchive.hpp b/include/boost/archive/portable_iarchive.hpp index b1482730a0..2fd9824849 100755 --- a/include/boost/archive/portable_iarchive.hpp +++ b/include/boost/archive/portable_iarchive.hpp @@ -159,296 +159,298 @@ namespace endian = boost::spirit::detail; #error "VAX floating point format is not supported!" #endif -namespace eos { +namespace boost { + namespace archive { - // forward declaration - class portable_iarchive; + // forward declaration + class portable_iarchive; - typedef boost::archive::basic_binary_iprimitive< - portable_iarchive - #if BOOST_VERSION < 103400 - , std::istream - #else - , std::istream::char_type - , std::istream::traits_type - #endif - > portable_iprimitive; - - /** - * \brief Portable binary input archive using little endian format. - * - * This archive addresses integer size, endianness and floating point types so - * that data can be transferred across different systems. There may still be - * constraints as to what systems are compatible and the user will have to take - * care that e.g. a very large int being saved on a 64 bit machine will result - * in a portable_archive_exception if loaded into an int on a 32 bit system. - * A possible workaround to this would be to use fixed types like - * boost::uint64_t in your serialization structures. - * - * \note The class is based on the portable binary example by Robert Ramey and - * uses Beman Dawes endian library plus fp_utilities by Johan Rade. - */ - class portable_iarchive : public portable_iprimitive - - // the example derives from common_oarchive but that lacks the - // load_override functions so we chose to stay one level higher - , public boost::archive::basic_binary_iarchive - - #if BOOST_VERSION >= 103500 && BOOST_VERSION < 105600 - // mix-in helper class for serializing shared_ptr - , public boost::archive::detail::shared_ptr_helper - #endif - { - // only needed for Robert's hack in basic_binary_iarchive::init - friend class boost::archive::basic_binary_iarchive; - - // workaround for gcc: use a dummy struct - // as additional argument type for overloading - template struct dummy { dummy(int) {}}; - - // loads directly from stream - inline signed char load_signed_char() - { - signed char c; - portable_iprimitive::load(c); - return c; - } - - // archive initialization - void init(unsigned flags) - { - using namespace boost::archive; - archive_version_type input_library_version(3); + typedef basic_binary_iprimitive < + portable_iarchive +#if BOOST_VERSION < 103400 + , std::istream +#else + , std::istream::char_type + , std::istream::traits_type +#endif + > portable_iprimitive; - // it is vital to have version information! - // if we don't have any we assume boost 1.33 - if (flags & no_header) - set_library_version(input_library_version); + /** + * \brief Portable binary input archive using little endian format. + * + * This archive addresses integer size, endianness and floating point types so + * that data can be transferred across different systems. There may still be + * constraints as to what systems are compatible and the user will have to take + * care that e.g. a very large int being saved on a 64 bit machine will result + * in a portable_archive_exception if loaded into an int on a 32 bit system. + * A possible workaround to this would be to use fixed types like + * boost::uint64_t in your serialization structures. + * + * \note The class is based on the portable binary example by Robert Ramey and + * uses Beman Dawes endian library plus fp_utilities by Johan Rade. + */ + class portable_iarchive : public portable_iprimitive - // extract and check the magic eos byte - else if (load_signed_char() != magic_byte) - throw archive_exception(archive_exception::invalid_signature); + // the example derives from common_oarchive but that lacks the + // load_override functions so we chose to stay one level higher + , public basic_binary_iarchive - else - { - // extract version information - operator>>(input_library_version); +#if BOOST_VERSION >= 103500 && BOOST_VERSION < 105600 + // mix-in helper class for serializing shared_ptr + , public detail::shared_ptr_helper +#endif + { + // only needed for Robert's hack in basic_binary_iarchive::init + friend class basic_binary_iarchive; - // throw if file version is newer than we are - if (input_library_version > archive_version) - throw archive_exception(archive_exception::unsupported_version); + // workaround for gcc: use a dummy struct + // as additional argument type for overloading + template struct dummy { dummy(int) {} }; - // else set the library version accordingly - else set_library_version(input_library_version); + // loads directly from stream + inline signed char load_signed_char() + { + signed char c; + portable_iprimitive::load(c); + return c; } - } - public: - /** - * \brief Constructor on a stream using ios::binary mode! - * - * We cannot call basic_binary_iprimitive::init which tries to detect - * if the binary archive stems from a different platform by examining - * type sizes. - * - * We could have called basic_binary_iarchive::init which would create - * the boost::serialization standard archive header containing also the - * library version. Due to efficiency we stick with our own. - */ - portable_iarchive(std::istream& is, unsigned flags = 0) - #if BOOST_VERSION < 103400 - : portable_iprimitive(is, flags & boost::archive::no_codecvt) - #else - : portable_iprimitive(*is.rdbuf(), flags & boost::archive::no_codecvt) - #endif - , boost::archive::basic_binary_iarchive(flags) - { - init(flags); - } + // archive initialization + void init(unsigned flags) + { + using namespace boost::archive; + archive_version_type input_library_version(3); + + // it is vital to have version information! + // if we don't have any we assume boost 1.33 + if (flags & no_header) + set_library_version(input_library_version); + + // extract and check the magic eos byte + else if (load_signed_char() != magic_byte) + throw archive_exception(archive_exception::invalid_signature); + + else + { + // extract version information + operator>>(input_library_version); + + // throw if file version is newer than we are + if (input_library_version > archive_version) + throw archive_exception(archive_exception::unsupported_version); + + // else set the library version accordingly + else set_library_version(input_library_version); + } + } - #if BOOST_VERSION >= 103400 - portable_iarchive(std::streambuf& sb, unsigned flags = 0) - : portable_iprimitive(sb, flags & boost::archive::no_codecvt) - , boost::archive::basic_binary_iarchive(flags) - { - init(flags); - } - #endif + public: + /** + * \brief Constructor on a stream using ios::binary mode! + * + * We cannot call basic_binary_iprimitive::init which tries to detect + * if the binary archive stems from a different platform by examining + * type sizes. + * + * We could have called basic_binary_iarchive::init which would create + * the boost::serialization standard archive header containing also the + * library version. Due to efficiency we stick with our own. + */ + portable_iarchive(std::istream& is, unsigned flags = 0) +#if BOOST_VERSION < 103400 + : portable_iprimitive(is, flags & no_codecvt) +#else + : portable_iprimitive(*is.rdbuf(), flags & no_codecvt) +#endif + , basic_binary_iarchive(flags) + { + init(flags); + } - //! Load narrow strings. - void load(std::string& s) - { - portable_iprimitive::load(s); - } +#if BOOST_VERSION >= 103400 + portable_iarchive(std::streambuf& sb, unsigned flags = 0) + : portable_iprimitive(sb, flags & no_codecvt) + , basic_binary_iarchive(flags) + { + init(flags); + } +#endif - #ifndef BOOST_NO_STD_WSTRING - /** - * \brief Load wide strings. - * - * This is rather tricky to get right for true portability as there - * are so many different character encodings around. However, wide - * strings that are encoded in one of the Unicode schemes only need - * to be _transcoded_ which is a lot easier actually. - * - * We generate the output string to be encoded in the system's native - * format, ie. UTF-16 on Windows and UTF-32 on Linux machines. Don't - * know about Mac here so I can't really say about that. - */ - void load(std::wstring& s) - { - std::string utf8; - load(utf8); - s = boost::from_utf8(utf8); - } - #endif + //! Load narrow strings. + void load(std::string& s) + { + portable_iprimitive::load(s); + } - /** - * \brief Loading bool type. - * - * Byte pattern is same as with integer types, so this function - * is somewhat redundant but treating bool as integer generates - * a lot of compiler warnings. - * - * \note If you cannot compile your application and it says something - * about load(bool) cannot convert your type A& into bool& then you - * should check your BOOST_CLASS_IMPLEMENTATION setting for A, as - * portable_archive is not able to handle custom primitive types in - * a general manner. - */ - void load(bool& b) - { - switch (signed char c = load_signed_char()) +#ifndef BOOST_NO_STD_WSTRING + /** + * \brief Load wide strings. + * + * This is rather tricky to get right for true portability as there + * are so many different character encodings around. However, wide + * strings that are encoded in one of the Unicode schemes only need + * to be _transcoded_ which is a lot easier actually. + * + * We generate the output string to be encoded in the system's native + * format, ie. UTF-16 on Windows and UTF-32 on Linux machines. Don't + * know about Mac here so I can't really say about that. + */ + void load(std::wstring& s) { - case 0: b = false; break; - case 1: b = load_signed_char(); break; - default: throw portable_archive_exception(c); + std::string utf8; + load(utf8); + s = boost::from_utf8(utf8); } - } +#endif - /** - * \brief Load integer types. - * - * First we load the size information ie. the number of bytes that - * hold the actual data. Then we retrieve the data and transform it - * to the original value by using load_little_endian. - */ - template - typename boost::enable_if >::type - load(T & t, dummy<2> = 0) - { - // get the number of bytes in the stream - if (signed char size = load_signed_char()) + /** + * \brief Loading bool type. + * + * Byte pattern is same as with integer types, so this function + * is somewhat redundant but treating bool as integer generates + * a lot of compiler warnings. + * + * \note If you cannot compile your application and it says something + * about load(bool) cannot convert your type A& into bool& then you + * should check your BOOST_CLASS_IMPLEMENTATION setting for A, as + * portable_archive is not able to handle custom primitive types in + * a general manner. + */ + void load(bool& b) { - // check for negative value in unsigned type - if (size < 0 && boost::is_unsigned::value) - throw portable_archive_exception(); - - // check that our type T is large enough - else if ((unsigned) abs(size) > sizeof(T)) - throw portable_archive_exception(size); - - // reconstruct the value - T temp = size < 0 ? -1 : 0; - load_binary(&temp, abs(size)); - #if BOOST_VERSION < 105800 - // load the value from little endian - it is then converted - // to the target type T and fits it because size <= sizeof(T) - t = endian::load_little_endian(&temp); - #else - // use new endian library from boost 1.58 - t = boost::endian::little_to_native(temp); - #endif + switch (signed char c = load_signed_char()) + { + case 0: b = false; break; + case 1: b = load_signed_char(); break; + default: throw portable_archive_exception(c); + } } - else t = 0; // zero optimization - } + /** + * \brief Load integer types. + * + * First we load the size information ie. the number of bytes that + * hold the actual data. Then we retrieve the data and transform it + * to the original value by using load_little_endian. + */ + template + typename boost::enable_if >::type + load(T & t, dummy<2> = 0) + { + // get the number of bytes in the stream + if (signed char size = load_signed_char()) + { + // check for negative value in unsigned type + if (size < 0 && boost::is_unsigned::value) + throw portable_archive_exception(); + + // check that our type T is large enough + else if ((unsigned)abs(size) > sizeof(T)) + throw portable_archive_exception(size); + + // reconstruct the value + T temp = size < 0 ? -1 : 0; + load_binary(&temp, abs(size)); +#if BOOST_VERSION < 105800 + // load the value from little endian - it is then converted + // to the target type T and fits it because size <= sizeof(T) + t = endian::load_little_endian(&temp); +#else + // use new endian library from boost 1.58 + t = boost::endian::little_to_native(temp); +#endif + } - /** - * \brief Load floating point types. - * - * We simply rely on fp_traits to set the bit pattern from the (unsigned) - * integral type that was stored in the stream. Francois Mauger provided - * standardized behaviour for special values like inf and NaN, that need to - * be serialized in his application. - * - * \note by Johan Rade (author of the floating point utilities library): - * Be warned that the math::detail::fp_traits::type::get_bits() function - * is *not* guaranteed to give you all bits of the floating point number. It - * will give you all bits if and only if there is an integer type that has - * the same size as the floating point you are copying from. It will not - * give you all bits for double if there is no uint64_t. It will not give - * you all bits for long double if sizeof(long double) > 8 or there is no - * uint64_t. - * - * The member fp_traits::type::coverage will tell you whether all bits - * are copied. This is a typedef for either math::detail::all_bits or - * math::detail::not_all_bits. - * - * If the function does not copy all bits, then it will copy the most - * significant bits. So if you serialize and deserialize the way you - * describe, and fp_traits::type::coverage is math::detail::not_all_bits, - * then your floating point numbers will be truncated. This will introduce - * small rounding off errors. - */ - template - typename boost::enable_if >::type - load(T & t, dummy<3> = 0) - { - typedef typename fp::detail::fp_traits::type traits; - - // if you end here there are three possibilities: - // 1. you're serializing a long double which is not portable - // 2. you're serializing a double but have no 64 bit integer - // 3. your machine is using an unknown floating point format - // after reading the note above you still might decide to - // deactivate this static assert and try if it works out. - typename traits::bits bits; - BOOST_STATIC_ASSERT(sizeof(bits) == sizeof(T)); - BOOST_STATIC_ASSERT(std::numeric_limits::is_iec559); - - load(bits); - traits::set_bits(t, bits); - - // if the no_infnan flag is set we must throw here - if (get_flags() & no_infnan && !fp::isfinite(t)) - throw portable_archive_exception(t); - - // if you end here your floating point type does not support - // denormalized numbers. this might be the case even though - // your type conforms to IEC 559 (and thus to IEEE 754) - if (std::numeric_limits::has_denorm == std::denorm_absent - && fp::fpclassify(t) == (int) FP_SUBNORMAL) // GCC4 - throw portable_archive_exception(t); - } - - // in boost 1.44 version_type was splitted into library_version_type and - // item_version_type, plus a whole bunch of additional strong typedefs. - template - typename boost::disable_if >::type - load(T& t, dummy<4> = 0) - { - // we provide a generic load routine for all types that feature - // conversion operators into an unsigned integer value like those - // created through BOOST_STRONG_TYPEDEF(X, some unsigned int) like - // library_version_type, collection_size_type, item_version_type, - // class_id_type, object_id_type, version_type and tracking_type - load((typename boost::uint_t::least&)(t)); - } - }; + else t = 0; // zero optimization + } + + /** + * \brief Load floating point types. + * + * We simply rely on fp_traits to set the bit pattern from the (unsigned) + * integral type that was stored in the stream. Francois Mauger provided + * standardized behaviour for special values like inf and NaN, that need to + * be serialized in his application. + * + * \note by Johan Rade (author of the floating point utilities library): + * Be warned that the math::detail::fp_traits::type::get_bits() function + * is *not* guaranteed to give you all bits of the floating point number. It + * will give you all bits if and only if there is an integer type that has + * the same size as the floating point you are copying from. It will not + * give you all bits for double if there is no uint64_t. It will not give + * you all bits for long double if sizeof(long double) > 8 or there is no + * uint64_t. + * + * The member fp_traits::type::coverage will tell you whether all bits + * are copied. This is a typedef for either math::detail::all_bits or + * math::detail::not_all_bits. + * + * If the function does not copy all bits, then it will copy the most + * significant bits. So if you serialize and deserialize the way you + * describe, and fp_traits::type::coverage is math::detail::not_all_bits, + * then your floating point numbers will be truncated. This will introduce + * small rounding off errors. + */ + template + typename boost::enable_if >::type + load(T & t, dummy<3> = 0) + { + typedef typename fp::detail::fp_traits::type traits; + + // if you end here there are three possibilities: + // 1. you're serializing a long double which is not portable + // 2. you're serializing a double but have no 64 bit integer + // 3. your machine is using an unknown floating point format + // after reading the note above you still might decide to + // deactivate this static assert and try if it works out. + typename traits::bits bits; + BOOST_STATIC_ASSERT(sizeof(bits) == sizeof(T)); + BOOST_STATIC_ASSERT(std::numeric_limits::is_iec559); + + load(bits); + traits::set_bits(t, bits); + + // if the no_infnan flag is set we must throw here + if (get_flags() & no_infnan && !fp::isfinite(t)) + throw portable_archive_exception(t); + + // if you end here your floating point type does not support + // denormalized numbers. this might be the case even though + // your type conforms to IEC 559 (and thus to IEEE 754) + if (std::numeric_limits::has_denorm == std::denorm_absent + && fp::fpclassify(t) == (int)FP_SUBNORMAL) // GCC4 + throw portable_archive_exception(t); + } - // polymorphic portable binary iarchive typedef - typedef POLYMORPHIC(portable_iarchive) polymorphic_portable_iarchive; - #undef POLYMORPHIC + // in boost 1.44 version_type was splitted into library_version_type and + // item_version_type, plus a whole bunch of additional strong typedefs. + template + typename boost::disable_if >::type + load(T& t, dummy<4> = 0) + { + // we provide a generic load routine for all types that feature + // conversion operators into an unsigned integer value like those + // created through BOOST_STRONG_TYPEDEF(X, some unsigned int) like + // library_version_type, collection_size_type, item_version_type, + // class_id_type, object_id_type, version_type and tracking_type + load((typename boost::uint_t::least&)(t)); + } + }; -} // namespace eos + // polymorphic portable binary iarchive typedef + typedef POLYMORPHIC(portable_iarchive) polymorphic_portable_iarchive; +#undef POLYMORPHIC + + } // namespace archive +} // namespace boost // this is required by export which registers all of your // classes with all the inbuilt archives plus our archive. #if BOOST_VERSION < 103500 -#define BOOST_ARCHIVE_CUSTOM_IARCHIVE_TYPES eos::portable_iarchive +#define BOOST_ARCHIVE_CUSTOM_IARCHIVE_TYPES boost::archive::portable_iarchive #else -BOOST_SERIALIZATION_REGISTER_ARCHIVE(eos::portable_iarchive) -BOOST_SERIALIZATION_REGISTER_ARCHIVE(eos::polymorphic_portable_iarchive) +BOOST_SERIALIZATION_REGISTER_ARCHIVE(boost::archive::portable_iarchive) +BOOST_SERIALIZATION_REGISTER_ARCHIVE(boost::archive::polymorphic_portable_iarchive) #endif // if you include this header multiple times and your compiler is picky @@ -470,10 +472,10 @@ BOOST_SERIALIZATION_REGISTER_ARCHIVE(eos::polymorphic_portable_iarchive) namespace boost { namespace archive { // explicitly instantiate for this type of binary stream - template class basic_binary_iarchive; + template class basic_binary_iarchive; template class basic_binary_iprimitive< - eos::portable_iarchive + portable_iarchive #if BOOST_VERSION < 103400 , std::istream #else @@ -483,9 +485,9 @@ namespace boost { namespace archive { >; #if BOOST_VERSION < 104000 - template class detail::archive_pointer_iserializer; + template class detail::archive_pointer_iserializer; #else - template class detail::archive_serializer_map; + template class detail::archive_serializer_map; //template class detail::archive_serializer_map; #endif diff --git a/include/boost/archive/portable_oarchive.hpp b/include/boost/archive/portable_oarchive.hpp index 2e26100a6a..047d8ecb74 100755 --- a/include/boost/archive/portable_oarchive.hpp +++ b/include/boost/archive/portable_oarchive.hpp @@ -162,276 +162,277 @@ namespace endian = boost::spirit::detail; #error "VAX floating point format is not supported!" #endif -namespace eos { +namespace boost { + namespace archive { - // forward declaration - class portable_oarchive; + // forward declaration + class portable_oarchive; - typedef boost::archive::basic_binary_oprimitive< - portable_oarchive - #if BOOST_VERSION < 103400 - , std::ostream - #else - , std::ostream::char_type - , std::ostream::traits_type - #endif - > portable_oprimitive; - - /** - * \brief Portable binary output archive using little endian format. - * - * This archive addresses integer size, endianness and floating point types so - * that data can be transferred across different systems. The archive consists - * primarily of three different save implementations for integral types, - * floating point types and string types. Those functions are templates and use - * enable_if to be correctly selected for overloading. - * - * \note The class is based on the portable binary example by Robert Ramey and - * uses Beman Dawes endian library plus fp_utilities by Johan Rade. - */ - class portable_oarchive : public portable_oprimitive - - // the example derives from common_oarchive but that lacks the - // save_override functions so we chose to stay one level higher - , public boost::archive::basic_binary_oarchive - - #if BOOST_VERSION >= 103500 && BOOST_VERSION < 105600 - // mix-in helper class for serializing shared_ptr - , public boost::archive::detail::shared_ptr_helper - #endif - { - // workaround for gcc: use a dummy struct - // as additional argument type for overloading - template struct dummy { dummy(int) {}}; - - // stores a signed char directly to stream - inline void save_signed_char(const signed char& c) - { - portable_oprimitive::save(c); - } - - // archive initialization - void init(unsigned flags) - { - // it is vital to have version information if the archive is - // to be parsed with a newer version of boost::serialization - // therefor we create a header, no header means boost 1.33 - if (flags & boost::archive::no_header) - BOOST_ASSERT(archive_version == 3); - else - { - // write our minimalistic header (magic byte plus version) - // the boost archives write a string instead - by calling - // boost::archive::basic_binary_oarchive::init() - save_signed_char(magic_byte); - - // write current version -// save(archive_version); - operator<<(archive_version); - } - } + typedef basic_binary_oprimitive < + portable_oarchive +#if BOOST_VERSION < 103400 + , std::ostream +#else + , std::ostream::char_type + , std::ostream::traits_type +#endif + > portable_oprimitive; - public: /** - * \brief Constructor on a stream using ios::binary mode! + * \brief Portable binary output archive using little endian format. * - * We cannot call basic_binary_oprimitive::init which stores type - * sizes to the archive in order to detect transfers to non-compatible - * platforms. + * This archive addresses integer size, endianness and floating point types so + * that data can be transferred across different systems. The archive consists + * primarily of three different save implementations for integral types, + * floating point types and string types. Those functions are templates and use + * enable_if to be correctly selected for overloading. * - * We could have called basic_binary_oarchive::init which would create - * the boost::serialization standard archive header containing also the - * library version. Due to efficiency we stick with our own. + * \note The class is based on the portable binary example by Robert Ramey and + * uses Beman Dawes endian library plus fp_utilities by Johan Rade. */ - portable_oarchive(std::ostream& os, unsigned flags = 0) - #if BOOST_VERSION < 103400 - : portable_oprimitive(os, flags & boost::archive::no_codecvt) - #else - : portable_oprimitive(*os.rdbuf(), flags & boost::archive::no_codecvt) - #endif - , boost::archive::basic_binary_oarchive(flags) - { - init(flags); - } + class portable_oarchive : public portable_oprimitive - #if BOOST_VERSION >= 103400 - portable_oarchive(std::streambuf& sb, unsigned flags = 0) - : portable_oprimitive(sb, flags & boost::archive::no_codecvt) - , boost::archive::basic_binary_oarchive(flags) - { - init(flags); - } - #endif + // the example derives from common_oarchive but that lacks the + // save_override functions so we chose to stay one level higher + , public basic_binary_oarchive - //! Save narrow strings. - void save(const std::string& s) +#if BOOST_VERSION >= 103500 && BOOST_VERSION < 105600 + // mix-in helper class for serializing shared_ptr + , public detail::shared_ptr_helper +#endif { - portable_oprimitive::save(s); - } + // workaround for gcc: use a dummy struct + // as additional argument type for overloading + template struct dummy { dummy(int) {} }; - #ifndef BOOST_NO_STD_WSTRING - /** - * \brief Save wide strings. - * - * This is rather tricky to get right for true portability as there - * are so many different character encodings around. However, wide - * strings that are encoded in one of the Unicode schemes only need - * to be _transcoded_ which is a lot easier actually. - * - * We expect the input string to be encoded in the system's native - * format, ie. UTF-16 on Windows and UTF-32 on Linux machines. Don't - * know about Mac here so I can't really say about that. - */ - void save(const std::wstring& s) - { - save(boost::to_utf8(s)); - } - #endif + // stores a signed char directly to stream + inline void save_signed_char(const signed char& c) + { + portable_oprimitive::save(c); + } - /** - * \brief Saving bool type. - * - * Saving bool directly, not by const reference - * because of tracking_type's operator (bool). - * - * \note If you cannot compile your application and it says something - * about save(bool) cannot convert your type const A& into bool then - * you should check your BOOST_CLASS_IMPLEMENTATION setting for A, as - * portable_archive is not able to handle custom primitive types in - * a general manner. - */ - void save(const bool& b) - { - save_signed_char(b); - if (b) save_signed_char('T'); - } + // archive initialization + void init(unsigned flags) + { + // it is vital to have version information if the archive is + // to be parsed with a newer version of boost::serialization + // therefor we create a header, no header means boost 1.33 + if (flags & no_header) + BOOST_ASSERT(archive_version == 3); + else + { + // write our minimalistic header (magic byte plus version) + // the boost archives write a string instead - by calling + // boost::archive::basic_binary_oarchive::init() + save_signed_char(magic_byte); + + // write current version + // save(archive_version); + operator<<(archive_version); + } + } - /** - * \brief Save integer types. - * - * First we save the size information ie. the number of bytes that hold the - * actual data. We subsequently transform the data using store_little_endian - * and store non-zero bytes to the stream. - */ - template - typename boost::enable_if >::type - save(const T & t, dummy<2> = 0) - { - if (T temp = t) + public: + /** + * \brief Constructor on a stream using ios::binary mode! + * + * We cannot call basic_binary_oprimitive::init which stores type + * sizes to the archive in order to detect transfers to non-compatible + * platforms. + * + * We could have called basic_binary_oarchive::init which would create + * the boost::serialization standard archive header containing also the + * library version. Due to efficiency we stick with our own. + */ + portable_oarchive(std::ostream& os, unsigned flags = 0) +#if BOOST_VERSION < 103400 + : portable_oprimitive(os, flags & no_codecvt) +#else + : portable_oprimitive(*os.rdbuf(), flags & no_codecvt) +#endif + , basic_binary_oarchive(flags) { - // examine the number of bytes - // needed to represent the number - signed char size = 0; - do { temp >>= CHAR_BIT; ++size; } - while (temp != 0 && temp != (T) -1); - - // encode the sign bit into the size - save_signed_char(t > 0 ? size : -size); - BOOST_ASSERT(t > 0 || boost::is_signed::value); - #if BOOST_VERSION < 105800 - // we choose to use little endian because this way we just - // save the first size bytes to the stream and skip the rest - endian::store_little_endian(&temp, t); - #else - // use new endian library from boost 1.58 - temp = boost::endian::native_to_little(t); - #endif - - save_binary(&temp, size); + init(flags); } - // zero optimization - else save_signed_char(0); - } - /** - * \brief Save floating point types. - * - * We simply rely on fp_traits to extract the bit pattern into an (unsigned) - * integral type and store that into the stream. Francois Mauger provided - * standardized behaviour for special values like inf and NaN, that need to - * be serialized in his application. - * - * \note by Johan Rade (author of the floating point utilities library): - * Be warned that the math::detail::fp_traits::type::get_bits() function - * is *not* guaranteed to give you all bits of the floating point number. It - * will give you all bits if and only if there is an integer type that has - * the same size as the floating point you are copying from. It will not - * give you all bits for double if there is no uint64_t. It will not give - * you all bits for long double if sizeof(long double) > 8 or there is no - * uint64_t. - * - * The member fp_traits::type::coverage will tell you whether all bits - * are copied. This is a typedef for either math::detail::all_bits or - * math::detail::not_all_bits. - * - * If the function does not copy all bits, then it will copy the most - * significant bits. So if you serialize and deserialize the way you - * describe, and fp_traits::type::coverage is math::detail::not_all_bits, - * then your floating point numbers will be truncated. This will introduce - * small rounding off errors. - */ - template - typename boost::enable_if >::type - save(const T & t, dummy<3> = 0) - { - typedef typename fp::detail::fp_traits::type traits; - - // if the no_infnan flag is set we must throw here - if (get_flags() & no_infnan && !fp::isfinite(t)) - throw portable_archive_exception(t); - - // if you end here there are three possibilities: - // 1. you're serializing a long double which is not portable - // 2. you're serializing a double but have no 64 bit integer - // 3. your machine is using an unknown floating point format - // after reading the note above you still might decide to - // deactivate this static assert and try if it works out. - typename traits::bits bits; - BOOST_STATIC_ASSERT(sizeof(bits) == sizeof(T)); - BOOST_STATIC_ASSERT(std::numeric_limits::is_iec559); - - // examine value closely - switch (fp::fpclassify(t)) +#if BOOST_VERSION >= 103400 + portable_oarchive(std::streambuf& sb, unsigned flags = 0) + : portable_oprimitive(sb, flags & no_codecvt) + , basic_binary_oarchive(flags) { - //case FP_ZERO: bits = 0; break; - case FP_NAN: bits = traits::exponent | traits::mantissa; break; - case FP_INFINITE: bits = traits::exponent | (t<0) * traits::sign; break; - case FP_SUBNORMAL: assert(std::numeric_limits::has_denorm); // pass - case FP_ZERO: // note that floats can be ±0.0 - case FP_NORMAL: traits::get_bits(t, bits); break; - default: throw portable_archive_exception(t); + init(flags); } +#endif - save(bits); - } + //! Save narrow strings. + void save(const std::string& s) + { + portable_oprimitive::save(s); + } - // in boost 1.44 version_type was splitted into library_version_type and - // item_version_type, plus a whole bunch of additional strong typedefs. - template - typename boost::disable_if >::type - save(const T& t, dummy<4> = 0) - { - // we provide a generic save routine for all types that feature - // conversion operators into an unsigned integer value like those - // created through BOOST_STRONG_TYPEDEF(X, some unsigned int) like - // library_version_type, collection_size_type, item_version_type, - // class_id_type, object_id_type, version_type and tracking_type - save((typename boost::uint_t::least)(t)); - } - }; +#ifndef BOOST_NO_STD_WSTRING + /** + * \brief Save wide strings. + * + * This is rather tricky to get right for true portability as there + * are so many different character encodings around. However, wide + * strings that are encoded in one of the Unicode schemes only need + * to be _transcoded_ which is a lot easier actually. + * + * We expect the input string to be encoded in the system's native + * format, ie. UTF-16 on Windows and UTF-32 on Linux machines. Don't + * know about Mac here so I can't really say about that. + */ + void save(const std::wstring& s) + { + save(boost::to_utf8(s)); + } +#endif + + /** + * \brief Saving bool type. + * + * Saving bool directly, not by const reference + * because of tracking_type's operator (bool). + * + * \note If you cannot compile your application and it says something + * about save(bool) cannot convert your type const A& into bool then + * you should check your BOOST_CLASS_IMPLEMENTATION setting for A, as + * portable_archive is not able to handle custom primitive types in + * a general manner. + */ + void save(const bool& b) + { + save_signed_char(b); + if (b) save_signed_char('T'); + } - // polymorphic portable binary oarchive typedef - typedef POLYMORPHIC(portable_oarchive) polymorphic_portable_oarchive; - #undef POLYMORPHIC + /** + * \brief Save integer types. + * + * First we save the size information ie. the number of bytes that hold the + * actual data. We subsequently transform the data using store_little_endian + * and store non-zero bytes to the stream. + */ + template + typename boost::enable_if >::type + save(const T & t, dummy<2> = 0) + { + if (T temp = t) + { + // examine the number of bytes + // needed to represent the number + signed char size = 0; + do { temp >>= CHAR_BIT; ++size; } while (temp != 0 && temp != (T)-1); + + // encode the sign bit into the size + save_signed_char(t > 0 ? size : -size); + BOOST_ASSERT(t > 0 || boost::is_signed::value); +#if BOOST_VERSION < 105800 + // we choose to use little endian because this way we just + // save the first size bytes to the stream and skip the rest + endian::store_little_endian(&temp, t); +#else + // use new endian library from boost 1.58 + temp = boost::endian::native_to_little(t); +#endif -} // namespace eos + save_binary(&temp, size); + } + // zero optimization + else save_signed_char(0); + } + + /** + * \brief Save floating point types. + * + * We simply rely on fp_traits to extract the bit pattern into an (unsigned) + * integral type and store that into the stream. Francois Mauger provided + * standardized behaviour for special values like inf and NaN, that need to + * be serialized in his application. + * + * \note by Johan Rade (author of the floating point utilities library): + * Be warned that the math::detail::fp_traits::type::get_bits() function + * is *not* guaranteed to give you all bits of the floating point number. It + * will give you all bits if and only if there is an integer type that has + * the same size as the floating point you are copying from. It will not + * give you all bits for double if there is no uint64_t. It will not give + * you all bits for long double if sizeof(long double) > 8 or there is no + * uint64_t. + * + * The member fp_traits::type::coverage will tell you whether all bits + * are copied. This is a typedef for either math::detail::all_bits or + * math::detail::not_all_bits. + * + * If the function does not copy all bits, then it will copy the most + * significant bits. So if you serialize and deserialize the way you + * describe, and fp_traits::type::coverage is math::detail::not_all_bits, + * then your floating point numbers will be truncated. This will introduce + * small rounding off errors. + */ + template + typename boost::enable_if >::type + save(const T & t, dummy<3> = 0) + { + typedef typename fp::detail::fp_traits::type traits; + + // if the no_infnan flag is set we must throw here + if (get_flags() & no_infnan && !fp::isfinite(t)) + throw portable_archive_exception(t); + + // if you end here there are three possibilities: + // 1. you're serializing a long double which is not portable + // 2. you're serializing a double but have no 64 bit integer + // 3. your machine is using an unknown floating point format + // after reading the note above you still might decide to + // deactivate this static assert and try if it works out. + typename traits::bits bits; + BOOST_STATIC_ASSERT(sizeof(bits) == sizeof(T)); + BOOST_STATIC_ASSERT(std::numeric_limits::is_iec559); + + // examine value closely + switch (fp::fpclassify(t)) + { + //case FP_ZERO: bits = 0; break; + case FP_NAN: bits = traits::exponent | traits::mantissa; break; + case FP_INFINITE: bits = traits::exponent | (t < 0) * traits::sign; break; + case FP_SUBNORMAL: assert(std::numeric_limits::has_denorm); // pass + case FP_ZERO: // note that floats can be ±0.0 + case FP_NORMAL: traits::get_bits(t, bits); break; + default: throw portable_archive_exception(t); + } + + save(bits); + } + + // in boost 1.44 version_type was splitted into library_version_type and + // item_version_type, plus a whole bunch of additional strong typedefs. + template + typename boost::disable_if >::type + save(const T& t, dummy<4> = 0) + { + // we provide a generic save routine for all types that feature + // conversion operators into an unsigned integer value like those + // created through BOOST_STRONG_TYPEDEF(X, some unsigned int) like + // library_version_type, collection_size_type, item_version_type, + // class_id_type, object_id_type, version_type and tracking_type + save((typename boost::uint_t::least)(t)); + } + }; + + // polymorphic portable binary oarchive typedef + typedef POLYMORPHIC(portable_oarchive) polymorphic_portable_oarchive; +#undef POLYMORPHIC + + } // namespace archive +} // namespace boost // required by export #if BOOST_VERSION < 103500 #define BOOST_ARCHIVE_CUSTOM_OARCHIVE_TYPES eos::portable_oarchive #else -BOOST_SERIALIZATION_REGISTER_ARCHIVE(eos::portable_oarchive) -BOOST_SERIALIZATION_REGISTER_ARCHIVE(eos::polymorphic_portable_oarchive) +BOOST_SERIALIZATION_REGISTER_ARCHIVE(boost::archive::portable_oarchive) +BOOST_SERIALIZATION_REGISTER_ARCHIVE(boost::archive::polymorphic_portable_oarchive) #endif // if you include this header multiple times and your compiler is picky @@ -453,10 +454,10 @@ BOOST_SERIALIZATION_REGISTER_ARCHIVE(eos::polymorphic_portable_oarchive) namespace boost { namespace archive { // explicitly instantiate for this type of binary stream - template class basic_binary_oarchive; + template class basic_binary_oarchive; template class basic_binary_oprimitive< - eos::portable_oarchive + portable_oarchive #if BOOST_VERSION < 103400 , std::ostream #else @@ -466,9 +467,9 @@ namespace boost { namespace archive { >; #if BOOST_VERSION < 104000 - template class detail::archive_pointer_oserializer; + template class detail::archive_pointer_oserializer; #else - template class detail::archive_serializer_map; + template class detail::archive_serializer_map; //template class detail::archive_serializer_map; #endif diff --git a/test/polymorphic_portable_archive.hpp b/test/polymorphic_portable_archive.hpp index 91d4cc3c0c..d87ce3ea4b 100755 --- a/test/polymorphic_portable_archive.hpp +++ b/test/polymorphic_portable_archive.hpp @@ -21,13 +21,13 @@ // include output archive header #include // set name of test output archive -typedef eos::polymorphic_portable_oarchive test_oarchive; +typedef boost::archive::polymorphic_portable_oarchive test_oarchive; // set name of test output stream typedef std::ofstream test_ostream; // repeat the above for input archive #include -typedef eos::polymorphic_portable_iarchive test_iarchive; +typedef boost::archive::polymorphic_portable_iarchive test_iarchive; typedef std::ifstream test_istream; // define open mode for streams diff --git a/test/portable_archive.hpp b/test/portable_archive.hpp index 95b0cda852..db851d3882 100644 --- a/test/portable_archive.hpp +++ b/test/portable_archive.hpp @@ -12,13 +12,13 @@ // #include output archive header #include // define output archive class to be used -typedef eos::portable_oarchive test_oarchive; +typedef boost::archive::portable_oarchive test_oarchive; // and corresponding stream typedef std::ofstream test_ostream; // repeat the above for correspondng input archive #include -typedef eos::portable_iarchive test_iarchive; +typedef boost::archive::portable_iarchive test_iarchive; typedef std::ifstream test_istream; // and stream open flags From c9deabb70349a1a743db6419474b5bc450944caa Mon Sep 17 00:00:00 2001 From: Christian Pfligersdorffer Date: Sat, 20 Feb 2016 23:39:14 +0100 Subject: [PATCH 4/5] Removed lots of #ifdefs needed for compilation against old boost versions --- .../archive/portable_archive_exception.hpp | 20 +- include/boost/archive/portable_iarchive.hpp | 567 ++++++++---------- include/boost/archive/portable_oarchive.hpp | 528 +++++++--------- 3 files changed, 479 insertions(+), 636 deletions(-) diff --git a/include/boost/archive/portable_archive_exception.hpp b/include/boost/archive/portable_archive_exception.hpp index a95dde0901..1e269c119f 100755 --- a/include/boost/archive/portable_archive_exception.hpp +++ b/include/boost/archive/portable_archive_exception.hpp @@ -18,30 +18,26 @@ #include #include +// hint from Johan Rade: on VMS there is still support for +// the VAX floating point format and this macro detects it +#if defined(__vms) && defined(__DECCXX) && !__IEEE_FLOAT +#error "VAX floating point format is not supported!" +#endif + namespace boost { namespace archive { // this value is written to the top of the stream - const signed char magic_byte = 'e' | 'o' | 's'; + const signed char magic_byte = 127; // flag for fp serialization const unsigned no_infnan = 64; // integral type for the archive version -#if BOOST_VERSION < 104400 - typedef version_type archive_version_type; -#else typedef library_version_type archive_version_type; -#endif // version of the linked boost archive library - const archive_version_type archive_version( -#if BOOST_VERSION < 103700 - ARCHIVE_VERSION() -#else - BOOST_ARCHIVE_VERSION() -#endif - ); + const archive_version_type archive_version(BOOST_ARCHIVE_VERSION()); /** * \brief Exception being thrown when serialization cannot proceed. diff --git a/include/boost/archive/portable_iarchive.hpp b/include/boost/archive/portable_iarchive.hpp index 2fd9824849..4c70600add 100755 --- a/include/boost/archive/portable_iarchive.hpp +++ b/include/boost/archive/portable_iarchive.hpp @@ -3,7 +3,7 @@ * \file portable_iarchive.hpp * \brief Provides an archive to read from portable binary files. * \author christian.pfligersdorffer@gmx.at - * \version 5.0 + * \version 6.0 * * This pair of archives brings the advantages of binary streams to the cross * platform boost::serialization user. While being almost as fast as the native @@ -23,6 +23,10 @@ * chance it will instantly work for your specific setup. If you encounter * problems or have suggestions please contact the author. * + * \note Version 6.0 is prepared for submission to boost serialization library. + * Full backwards compatibility is maintained for all your archived data! + * Namespaces changed and some refactoring was necessary, that's all. + * * \note Version 5.1 is now compatible with boost up to version 1.59. Thanks to * ecotax for pointing to the issue with shared_ptr_helper. * @@ -92,54 +96,17 @@ #include #include -#if BOOST_VERSION >= 103500 && BOOST_VERSION < 105600 -#include -#endif - // funny polymorphics -#if BOOST_VERSION < 103500 -#include -#define POLYMORPHIC(T) boost::archive::detail::polymorphic_iarchive_impl - -#elif BOOST_VERSION < 103600 -#include -#define POLYMORPHIC(T) boost::archive::detail::polymorphic_iarchive_dispatch - -#else #include -#define POLYMORPHIC(T) boost::archive::detail::polymorphic_iarchive_route -#endif // endian and fpclassify -#if BOOST_VERSION < 103600 -#include -#include -#elif BOOST_VERSION < 104800 -#include -#include -#elif BOOST_VERSION < 105800 -#include -#include -#else #include #include -#endif -// namespace alias -#if BOOST_VERSION < 103800 -namespace fp = boost::math; -#else +// namespace alias for fp utilities namespace fp = boost::spirit::math; -#endif -// namespace alias endian -#if BOOST_VERSION < 104800 -namespace endian = boost::detail; -#elif BOOST_VERSION < 105800 -namespace endian = boost::spirit::detail; -#endif - -#if BOOST_VERSION >= 104500 && !defined BOOST_NO_STD_WSTRING +#ifndef BOOST_NO_STD_WSTRING // used for wstring to utf8 conversion #include #include @@ -153,305 +120,271 @@ namespace endian = boost::spirit::detail; #include "portable_archive_exception.hpp" -// hint from Johan Rade: on VMS there is still support for -// the VAX floating point format and this macro detects it -#if defined(__vms) && defined(__DECCXX) && !__IEEE_FLOAT -#error "VAX floating point format is not supported!" -#endif - -namespace boost { - namespace archive { - - // forward declaration - class portable_iarchive; +namespace boost { namespace archive { - typedef basic_binary_iprimitive < - portable_iarchive -#if BOOST_VERSION < 103400 - , std::istream -#else - , std::istream::char_type - , std::istream::traits_type -#endif - > portable_iprimitive; + // forward declaration + class portable_iarchive; - /** - * \brief Portable binary input archive using little endian format. - * - * This archive addresses integer size, endianness and floating point types so - * that data can be transferred across different systems. There may still be - * constraints as to what systems are compatible and the user will have to take - * care that e.g. a very large int being saved on a 64 bit machine will result - * in a portable_archive_exception if loaded into an int on a 32 bit system. - * A possible workaround to this would be to use fixed types like - * boost::uint64_t in your serialization structures. - * - * \note The class is based on the portable binary example by Robert Ramey and - * uses Beman Dawes endian library plus fp_utilities by Johan Rade. - */ - class portable_iarchive : public portable_iprimitive - - // the example derives from common_oarchive but that lacks the - // load_override functions so we chose to stay one level higher - , public basic_binary_iarchive + typedef basic_binary_iprimitive < + portable_iarchive + , std::istream::char_type + , std::istream::traits_type + > portable_iprimitive; + + /** + * \brief Portable binary input archive using little endian format. + * + * This archive addresses integer size, endianness and floating point types so + * that data can be transferred across different systems. There may still be + * constraints as to what systems are compatible and the user will have to take + * care that e.g. a very large int being saved on a 64 bit machine will result + * in a portable_archive_exception if loaded into an int on a 32 bit system. + * A possible workaround to this would be to use fixed types like + * boost::uint64_t in your serialization structures. + * + * \note The class is based on the portable binary example by Robert Ramey and + * uses Beman Dawes endian library plus fp_utilities by Johan Rade. + */ + class portable_iarchive : public portable_iprimitive + + // Robert's example derives from common_oarchive but that lacks the + // load_override functions so we chose to stay one level higher + , public basic_binary_iarchive + { + // only needed for Robert's hack in basic_binary_iarchive::init + friend class basic_binary_iarchive; + + // workaround for gcc: use a dummy struct + // as additional argument type for overloading + template struct dummy { dummy(int) {} }; + + // loads directly from stream + inline signed char load_signed_char() + { + signed char c; + portable_iprimitive::load(c); + return c; + } -#if BOOST_VERSION >= 103500 && BOOST_VERSION < 105600 - // mix-in helper class for serializing shared_ptr - , public detail::shared_ptr_helper -#endif + // archive initialization + void init(unsigned flags) { - // only needed for Robert's hack in basic_binary_iarchive::init - friend class basic_binary_iarchive; + archive_version_type input_library_version(3); - // workaround for gcc: use a dummy struct - // as additional argument type for overloading - template struct dummy { dummy(int) {} }; + // it is vital to have version information! + // if we don't have any we assume boost 1.33 + if (flags & no_header) + set_library_version(input_library_version); - // loads directly from stream - inline signed char load_signed_char() - { - signed char c; - portable_iprimitive::load(c); - return c; - } + // extract and check the magic byte header + else if (load_signed_char() != magic_byte) + throw archive_exception(archive_exception::invalid_signature); - // archive initialization - void init(unsigned flags) + else { - using namespace boost::archive; - archive_version_type input_library_version(3); - - // it is vital to have version information! - // if we don't have any we assume boost 1.33 - if (flags & no_header) - set_library_version(input_library_version); - - // extract and check the magic eos byte - else if (load_signed_char() != magic_byte) - throw archive_exception(archive_exception::invalid_signature); - - else - { - // extract version information - operator>>(input_library_version); - - // throw if file version is newer than we are - if (input_library_version > archive_version) - throw archive_exception(archive_exception::unsupported_version); - - // else set the library version accordingly - else set_library_version(input_library_version); - } - } + // extract version information + operator>>(input_library_version); - public: - /** - * \brief Constructor on a stream using ios::binary mode! - * - * We cannot call basic_binary_iprimitive::init which tries to detect - * if the binary archive stems from a different platform by examining - * type sizes. - * - * We could have called basic_binary_iarchive::init which would create - * the boost::serialization standard archive header containing also the - * library version. Due to efficiency we stick with our own. - */ - portable_iarchive(std::istream& is, unsigned flags = 0) -#if BOOST_VERSION < 103400 - : portable_iprimitive(is, flags & no_codecvt) -#else - : portable_iprimitive(*is.rdbuf(), flags & no_codecvt) -#endif - , basic_binary_iarchive(flags) - { - init(flags); - } + // throw if file version is newer than we are + if (input_library_version > archive_version) + throw archive_exception(archive_exception::unsupported_version); -#if BOOST_VERSION >= 103400 - portable_iarchive(std::streambuf& sb, unsigned flags = 0) - : portable_iprimitive(sb, flags & no_codecvt) - , basic_binary_iarchive(flags) - { - init(flags); + // else set the library version accordingly + else set_library_version(input_library_version); } -#endif + } - //! Load narrow strings. - void load(std::string& s) - { - portable_iprimitive::load(s); - } + public: + /** + * \brief Constructor on a stream using ios::binary mode! + * + * We cannot call basic_binary_iprimitive::init which tries to detect + * if the binary archive stems from a different platform by examining + * type sizes. + * + * We could have called basic_binary_iarchive::init which would create + * the boost::serialization standard archive header containing also the + * library version. Due to efficiency we stick with our own. + */ + portable_iarchive(std::istream& is, unsigned flags = 0) + : portable_iprimitive(*is.rdbuf(), flags & no_codecvt) + , basic_binary_iarchive(flags) + { + init(flags); + } -#ifndef BOOST_NO_STD_WSTRING - /** - * \brief Load wide strings. - * - * This is rather tricky to get right for true portability as there - * are so many different character encodings around. However, wide - * strings that are encoded in one of the Unicode schemes only need - * to be _transcoded_ which is a lot easier actually. - * - * We generate the output string to be encoded in the system's native - * format, ie. UTF-16 on Windows and UTF-32 on Linux machines. Don't - * know about Mac here so I can't really say about that. - */ - void load(std::wstring& s) - { - std::string utf8; - load(utf8); - s = boost::from_utf8(utf8); - } -#endif + portable_iarchive(std::streambuf& sb, unsigned flags = 0) + : portable_iprimitive(sb, flags & no_codecvt) + , basic_binary_iarchive(flags) + { + init(flags); + } - /** - * \brief Loading bool type. - * - * Byte pattern is same as with integer types, so this function - * is somewhat redundant but treating bool as integer generates - * a lot of compiler warnings. - * - * \note If you cannot compile your application and it says something - * about load(bool) cannot convert your type A& into bool& then you - * should check your BOOST_CLASS_IMPLEMENTATION setting for A, as - * portable_archive is not able to handle custom primitive types in - * a general manner. - */ - void load(bool& b) - { - switch (signed char c = load_signed_char()) - { - case 0: b = false; break; - case 1: b = load_signed_char(); break; - default: throw portable_archive_exception(c); - } - } + //! Load narrow strings. + void load(std::string& s) + { + portable_iprimitive::load(s); + } - /** - * \brief Load integer types. - * - * First we load the size information ie. the number of bytes that - * hold the actual data. Then we retrieve the data and transform it - * to the original value by using load_little_endian. - */ - template - typename boost::enable_if >::type - load(T & t, dummy<2> = 0) - { - // get the number of bytes in the stream - if (signed char size = load_signed_char()) - { - // check for negative value in unsigned type - if (size < 0 && boost::is_unsigned::value) - throw portable_archive_exception(); - - // check that our type T is large enough - else if ((unsigned)abs(size) > sizeof(T)) - throw portable_archive_exception(size); - - // reconstruct the value - T temp = size < 0 ? -1 : 0; - load_binary(&temp, abs(size)); -#if BOOST_VERSION < 105800 - // load the value from little endian - it is then converted - // to the target type T and fits it because size <= sizeof(T) - t = endian::load_little_endian(&temp); -#else - // use new endian library from boost 1.58 - t = boost::endian::little_to_native(temp); +#ifndef BOOST_NO_STD_WSTRING + /** + * \brief Load wide strings. + * + * This is rather tricky to get right for true portability as there + * are so many different character encodings around. However, wide + * strings that are encoded in one of the Unicode schemes only need + * to be _transcoded_ which is a lot easier actually. + * + * We generate the output string to be encoded in the system's native + * format, ie. UTF-16 on Windows and UTF-32 on Linux machines. Don't + * know about Mac here so I can't really say about that. + */ + void load(std::wstring& s) + { + std::string utf8; + load(utf8); + s = boost::from_utf8(utf8); + } #endif - } - else t = 0; // zero optimization - } - - /** - * \brief Load floating point types. - * - * We simply rely on fp_traits to set the bit pattern from the (unsigned) - * integral type that was stored in the stream. Francois Mauger provided - * standardized behaviour for special values like inf and NaN, that need to - * be serialized in his application. - * - * \note by Johan Rade (author of the floating point utilities library): - * Be warned that the math::detail::fp_traits::type::get_bits() function - * is *not* guaranteed to give you all bits of the floating point number. It - * will give you all bits if and only if there is an integer type that has - * the same size as the floating point you are copying from. It will not - * give you all bits for double if there is no uint64_t. It will not give - * you all bits for long double if sizeof(long double) > 8 or there is no - * uint64_t. - * - * The member fp_traits::type::coverage will tell you whether all bits - * are copied. This is a typedef for either math::detail::all_bits or - * math::detail::not_all_bits. - * - * If the function does not copy all bits, then it will copy the most - * significant bits. So if you serialize and deserialize the way you - * describe, and fp_traits::type::coverage is math::detail::not_all_bits, - * then your floating point numbers will be truncated. This will introduce - * small rounding off errors. - */ - template - typename boost::enable_if >::type - load(T & t, dummy<3> = 0) + /** + * \brief Loading bool type. + * + * Byte pattern is same as with integer types, so this function + * is somewhat redundant but treating bool as integer generates + * a lot of compiler warnings. + * + * \note If you cannot compile your application and it says something + * about load(bool) cannot convert your type A& into bool& then you + * should check your BOOST_CLASS_IMPLEMENTATION setting for A, as + * portable_archive is not able to handle custom primitive types in + * a general manner. + */ + void load(bool& b) + { + switch (signed char c = load_signed_char()) { - typedef typename fp::detail::fp_traits::type traits; - - // if you end here there are three possibilities: - // 1. you're serializing a long double which is not portable - // 2. you're serializing a double but have no 64 bit integer - // 3. your machine is using an unknown floating point format - // after reading the note above you still might decide to - // deactivate this static assert and try if it works out. - typename traits::bits bits; - BOOST_STATIC_ASSERT(sizeof(bits) == sizeof(T)); - BOOST_STATIC_ASSERT(std::numeric_limits::is_iec559); - - load(bits); - traits::set_bits(t, bits); - - // if the no_infnan flag is set we must throw here - if (get_flags() & no_infnan && !fp::isfinite(t)) - throw portable_archive_exception(t); - - // if you end here your floating point type does not support - // denormalized numbers. this might be the case even though - // your type conforms to IEC 559 (and thus to IEEE 754) - if (std::numeric_limits::has_denorm == std::denorm_absent - && fp::fpclassify(t) == (int)FP_SUBNORMAL) // GCC4 - throw portable_archive_exception(t); + case 0: b = false; break; + case 1: b = load_signed_char(); break; + default: throw portable_archive_exception(c); } + } - // in boost 1.44 version_type was splitted into library_version_type and - // item_version_type, plus a whole bunch of additional strong typedefs. - template - typename boost::disable_if >::type - load(T& t, dummy<4> = 0) + /** + * \brief Load integer types. + * + * First we load the size information ie. the number of bytes that + * hold the actual data. Then we retrieve the data and transform it + * to the original value by using load_little_endian. + */ + template + typename boost::enable_if >::type + load(T & t, dummy<2> = 0) + { + // get the number of bytes in the stream + if (signed char size = load_signed_char()) { - // we provide a generic load routine for all types that feature - // conversion operators into an unsigned integer value like those - // created through BOOST_STRONG_TYPEDEF(X, some unsigned int) like - // library_version_type, collection_size_type, item_version_type, - // class_id_type, object_id_type, version_type and tracking_type - load((typename boost::uint_t::least&)(t)); + // check for negative value in unsigned type + if (size < 0 && boost::is_unsigned::value) + throw portable_archive_exception(); + + // check that our type T is large enough + else if ((unsigned)abs(size) > sizeof(T)) + throw portable_archive_exception(size); + + // reconstruct the value + T temp = size < 0 ? -1 : 0; + load_binary(&temp, abs(size)); + // load the value from little endian - it is then converted + // to the target type T and fits it because size <= sizeof(T) + t = boost::endian::little_to_native(temp); } - }; - // polymorphic portable binary iarchive typedef - typedef POLYMORPHIC(portable_iarchive) polymorphic_portable_iarchive; -#undef POLYMORPHIC + else t = 0; // zero optimization + } + + /** + * \brief Load floating point types. + * + * We simply rely on fp_traits to set the bit pattern from the (unsigned) + * integral type that was stored in the stream. Francois Mauger provided + * standardized behaviour for special values like inf and NaN, that need to + * be serialized in his application. + * + * \note by Johan Rade (author of the floating point utilities library): + * Be warned that the math::detail::fp_traits::type::get_bits() function + * is *not* guaranteed to give you all bits of the floating point number. It + * will give you all bits if and only if there is an integer type that has + * the same size as the floating point you are copying from. It will not + * give you all bits for double if there is no uint64_t. It will not give + * you all bits for long double if sizeof(long double) > 8 or there is no + * uint64_t. + * + * The member fp_traits::type::coverage will tell you whether all bits + * are copied. This is a typedef for either math::detail::all_bits or + * math::detail::not_all_bits. + * + * If the function does not copy all bits, then it will copy the most + * significant bits. So if you serialize and deserialize the way you + * describe, and fp_traits::type::coverage is math::detail::not_all_bits, + * then your floating point numbers will be truncated. This will introduce + * small rounding off errors. + */ + template + typename boost::enable_if >::type + load(T & t, dummy<3> = 0) + { + typedef typename fp::detail::fp_traits::type traits; + + // if you end here there are three possibilities: + // 1. you're serializing a long double which is not portable + // 2. you're serializing a double but have no 64 bit integer + // 3. your machine is using an unknown floating point format + // after reading the note above you still might decide to + // deactivate this static assert and try if it works out. + typename traits::bits bits; + BOOST_STATIC_ASSERT(sizeof(bits) == sizeof(T)); + BOOST_STATIC_ASSERT(std::numeric_limits::is_iec559); + + load(bits); + traits::set_bits(t, bits); + + // if the no_infnan flag is set we must throw here + if (get_flags() & no_infnan && !fp::isfinite(t)) + throw portable_archive_exception(t); + + // if you end here your floating point type does not support + // denormalized numbers. this might be the case even though + // your type conforms to IEC 559 (and thus to IEEE 754) + if (std::numeric_limits::has_denorm == std::denorm_absent + && fp::fpclassify(t) == (int)FP_SUBNORMAL) // GCC4 + throw portable_archive_exception(t); + } + + // in boost 1.44 version_type was splitted into library_version_type and + // item_version_type, plus a whole bunch of additional strong typedefs. + template + typename boost::disable_if >::type + load(T& t, dummy<4> = 0) + { + // we provide a generic load routine for all types that feature + // conversion operators into an unsigned integer value like those + // created through BOOST_STRONG_TYPEDEF(X, some unsigned int) ie. + // library_version_type, collection_size_type, item_version_type, + // class_id_type, object_id_type, version_type and tracking_type + load((typename boost::uint_t::least&)(t)); + } + }; + + // polymorphic portable binary iarchive typedef + typedef detail::polymorphic_iarchive_route polymorphic_portable_iarchive; - } // namespace archive -} // namespace boost +} } // namespace boost::archive // this is required by export which registers all of your // classes with all the inbuilt archives plus our archive. -#if BOOST_VERSION < 103500 -#define BOOST_ARCHIVE_CUSTOM_IARCHIVE_TYPES boost::archive::portable_iarchive -#else BOOST_SERIALIZATION_REGISTER_ARCHIVE(boost::archive::portable_iarchive) BOOST_SERIALIZATION_REGISTER_ARCHIVE(boost::archive::polymorphic_portable_iarchive) -#endif // if you include this header multiple times and your compiler is picky // about multiple template instantiations (eg. gcc is) then you need to @@ -462,9 +395,7 @@ BOOST_SERIALIZATION_REGISTER_ARCHIVE(boost::archive::polymorphic_portable_iarchi #include #include -#if BOOST_VERSION < 104000 -#include -#elif !defined BOOST_ARCHIVE_SERIALIZER_INCLUDED +#ifndef BOOST_ARCHIVE_SERIALIZER_INCLUDED #include #define BOOST_ARCHIVE_SERIALIZER_INCLUDED #endif @@ -476,20 +407,12 @@ namespace boost { namespace archive { template class basic_binary_iprimitive< portable_iarchive - #if BOOST_VERSION < 103400 - , std::istream - #else , std::istream::char_type , std::istream::traits_type - #endif >; -#if BOOST_VERSION < 104000 - template class detail::archive_pointer_iserializer; -#else + // need to instantiate this template also for polymorphic version? template class detail::archive_serializer_map; - //template class detail::archive_serializer_map; -#endif } } // namespace boost::archive diff --git a/include/boost/archive/portable_oarchive.hpp b/include/boost/archive/portable_oarchive.hpp index 047d8ecb74..b033119ef3 100755 --- a/include/boost/archive/portable_oarchive.hpp +++ b/include/boost/archive/portable_oarchive.hpp @@ -3,7 +3,7 @@ * \file portable_oarchive.hpp * \brief Provides an archive to create portable binary files. * \author christian.pfligersdorffer@gmx.at - * \version 5.1 + * \version 6.0 * * This pair of archives brings the advantages of binary streams to the cross * platform boost::serialization user. While being almost as fast as the native @@ -23,6 +23,10 @@ * chance it will instantly work for your specific setup. If you encounter * problems or have suggestions please contact the author. * + * \note Version 6.0 is prepared for submission to boost serialization library. + * Full backwards compatibility is maintained for all your archived data! + * Namespaces changed and some refactoring was necessary, that's all. + * * \note Version 5.1 is now compatible with boost up to version 1.59. Thanks to * ecotax for pointing to the issue with shared_ptr_helper. * @@ -94,55 +98,16 @@ #include #include #include - -#if BOOST_VERSION >= 103500 && BOOST_VERSION < 105600 -#include -#endif - -// funny polymorphics -#if BOOST_VERSION < 103500 -#include -#define POLYMORPHIC(T) boost::archive::detail::polymorphic_oarchive_impl - -#elif BOOST_VERSION < 103600 -#include -#define POLYMORPHIC(T) boost::archive::detail::polymorphic_oarchive_dispatch - -#else #include -#define POLYMORPHIC(T) boost::archive::detail::polymorphic_oarchive_route -#endif // endian and fpclassify -#if BOOST_VERSION < 103600 -#include -#include -#elif BOOST_VERSION < 104800 -#include -#include -#elif BOOST_VERSION < 105800 -#include -#include -#else #include #include -#endif -// namespace alias -#if BOOST_VERSION < 103800 -namespace fp = boost::math; -#else +// namespace alias for fp utilities namespace fp = boost::spirit::math; -#endif - -// namespace alias endian -#if BOOST_VERSION < 104800 -namespace endian = boost::detail; -#elif BOOST_VERSION < 105800 -namespace endian = boost::spirit::detail; -#endif -#if BOOST_VERSION >= 104500 && !defined BOOST_NO_STD_WSTRING +#ifndef BOOST_NO_STD_WSTRING // used for wstring to utf8 conversion #include #include @@ -156,284 +121,253 @@ namespace endian = boost::spirit::detail; #include "portable_archive_exception.hpp" -// hint from Johan Rade: on VMS there is still support for -// the VAX floating point format and this macro detects it -#if defined(__vms) && defined(__DECCXX) && !__IEEE_FLOAT -#error "VAX floating point format is not supported!" -#endif +namespace boost { namespace archive { -namespace boost { - namespace archive { + // forward declaration + class portable_oarchive; - // forward declaration - class portable_oarchive; + typedef basic_binary_oprimitive < + portable_oarchive + , std::ostream::char_type + , std::ostream::traits_type + > portable_oprimitive; + + /** + * \brief Portable binary output archive using little endian format. + * + * This archive addresses integer size, endianness and floating point types so + * that data can be transferred across different systems. The archive consists + * primarily of three different save implementations for integral types, + * floating point types and string types. Those functions are templates and use + * enable_if to be correctly selected for overloading. + * + * \note The class is based on the portable binary example by Robert Ramey and + * uses Beman Dawes endian library plus fp_utilities by Johan Rade. + */ + class portable_oarchive : public portable_oprimitive + + // Robert's example derives from common_oarchive but that lacks the + // save_override functions so we chose to stay one level higher + , public basic_binary_oarchive + { + // workaround for gcc: use a dummy struct + // as additional argument type for overloading + template struct dummy { dummy(int) {} }; + + // stores a signed char directly to stream + inline void save_signed_char(const signed char& c) + { + portable_oprimitive::save(c); + } - typedef basic_binary_oprimitive < - portable_oarchive -#if BOOST_VERSION < 103400 - , std::ostream -#else - , std::ostream::char_type - , std::ostream::traits_type -#endif - > portable_oprimitive; + // archive initialization + void init(unsigned flags) + { + // it is vital to have version information if the archive is + // to be parsed with a newer version of boost::serialization + // therefor we create a header, no header means boost 1.33 + // for backwards compatibility + if (flags & no_header) + BOOST_ASSERT(archive_version == 3); + else + { + // write our minimalistic header (magic byte plus version) + // the boost archives write a string instead - by calling + // boost::archive::basic_binary_oarchive::init() + save_signed_char(magic_byte); + + // write current version +// save(archive_version); + operator<<(archive_version); + } + } + public: /** - * \brief Portable binary output archive using little endian format. + * \brief Constructor on a stream using ios::binary mode! * - * This archive addresses integer size, endianness and floating point types so - * that data can be transferred across different systems. The archive consists - * primarily of three different save implementations for integral types, - * floating point types and string types. Those functions are templates and use - * enable_if to be correctly selected for overloading. + * We cannot call basic_binary_oprimitive::init which stores type + * sizes to the archive in order to detect transfers to non-compatible + * platforms. * - * \note The class is based on the portable binary example by Robert Ramey and - * uses Beman Dawes endian library plus fp_utilities by Johan Rade. + * We could have called basic_binary_oarchive::init which would create + * the boost::serialization standard archive header containing also the + * library version. Due to efficiency we stick with our own. */ - class portable_oarchive : public portable_oprimitive - - // the example derives from common_oarchive but that lacks the - // save_override functions so we chose to stay one level higher - , public basic_binary_oarchive - -#if BOOST_VERSION >= 103500 && BOOST_VERSION < 105600 - // mix-in helper class for serializing shared_ptr - , public detail::shared_ptr_helper -#endif + portable_oarchive(std::ostream& os, unsigned flags = 0) + : portable_oprimitive(*os.rdbuf(), flags & no_codecvt) + , basic_binary_oarchive(flags) { - // workaround for gcc: use a dummy struct - // as additional argument type for overloading - template struct dummy { dummy(int) {} }; + init(flags); + } - // stores a signed char directly to stream - inline void save_signed_char(const signed char& c) - { - portable_oprimitive::save(c); - } - - // archive initialization - void init(unsigned flags) - { - // it is vital to have version information if the archive is - // to be parsed with a newer version of boost::serialization - // therefor we create a header, no header means boost 1.33 - if (flags & no_header) - BOOST_ASSERT(archive_version == 3); - else - { - // write our minimalistic header (magic byte plus version) - // the boost archives write a string instead - by calling - // boost::archive::basic_binary_oarchive::init() - save_signed_char(magic_byte); - - // write current version - // save(archive_version); - operator<<(archive_version); - } - } + portable_oarchive(std::streambuf& sb, unsigned flags = 0) + : portable_oprimitive(sb, flags & no_codecvt) + , basic_binary_oarchive(flags) + { + init(flags); + } - public: - /** - * \brief Constructor on a stream using ios::binary mode! - * - * We cannot call basic_binary_oprimitive::init which stores type - * sizes to the archive in order to detect transfers to non-compatible - * platforms. - * - * We could have called basic_binary_oarchive::init which would create - * the boost::serialization standard archive header containing also the - * library version. Due to efficiency we stick with our own. - */ - portable_oarchive(std::ostream& os, unsigned flags = 0) -#if BOOST_VERSION < 103400 - : portable_oprimitive(os, flags & no_codecvt) -#else - : portable_oprimitive(*os.rdbuf(), flags & no_codecvt) -#endif - , basic_binary_oarchive(flags) - { - init(flags); - } + //! Save narrow strings. + void save(const std::string& s) + { + portable_oprimitive::save(s); + } -#if BOOST_VERSION >= 103400 - portable_oarchive(std::streambuf& sb, unsigned flags = 0) - : portable_oprimitive(sb, flags & no_codecvt) - , basic_binary_oarchive(flags) - { - init(flags); - } +#ifndef BOOST_NO_STD_WSTRING + /** + * \brief Save wide strings. + * + * This is rather tricky to get right for true portability as there + * are so many different character encodings around. However, wide + * strings that are encoded in one of the Unicode schemes only need + * to be _transcoded_ which is a lot easier actually. + * + * We expect the input string to be encoded in the system's native + * format, ie. UTF-16 on Windows and UTF-32 on Linux machines. Don't + * know about Mac here so I can't really say about that. + */ + void save(const std::wstring& s) + { + save(boost::to_utf8(s)); + } #endif - //! Save narrow strings. - void save(const std::string& s) - { - portable_oprimitive::save(s); - } + /** + * \brief Saving bool type. + * + * Saving bool directly, not by const reference + * because of tracking_type's operator (bool). + * + * \note If you cannot compile your application and it says something + * about save(bool) cannot convert your type const A& into bool then + * you should check your BOOST_CLASS_IMPLEMENTATION setting for A, as + * portable_archive is not able to handle custom primitive types in + * a general manner. + */ + void save(const bool& b) + { + save_signed_char(b); + if (b) save_signed_char('T'); + } -#ifndef BOOST_NO_STD_WSTRING - /** - * \brief Save wide strings. - * - * This is rather tricky to get right for true portability as there - * are so many different character encodings around. However, wide - * strings that are encoded in one of the Unicode schemes only need - * to be _transcoded_ which is a lot easier actually. - * - * We expect the input string to be encoded in the system's native - * format, ie. UTF-16 on Windows and UTF-32 on Linux machines. Don't - * know about Mac here so I can't really say about that. - */ - void save(const std::wstring& s) + /** + * \brief Save integer types. + * + * First we save the size information ie. the number of bytes that hold the + * actual data. We subsequently transform the data using store_little_endian + * and store non-zero bytes to the stream. + */ + template + typename boost::enable_if >::type + save(const T & t, dummy<2> = 0) + { + if (T temp = t) { - save(boost::to_utf8(s)); - } -#endif + // examine the number of bytes + // needed to represent the number + signed char size = 0; + do { temp >>= CHAR_BIT; ++size; } while (temp != 0 && temp != (T)-1); - /** - * \brief Saving bool type. - * - * Saving bool directly, not by const reference - * because of tracking_type's operator (bool). - * - * \note If you cannot compile your application and it says something - * about save(bool) cannot convert your type const A& into bool then - * you should check your BOOST_CLASS_IMPLEMENTATION setting for A, as - * portable_archive is not able to handle custom primitive types in - * a general manner. - */ - void save(const bool& b) - { - save_signed_char(b); - if (b) save_signed_char('T'); - } + // encode the sign bit into the size + save_signed_char(t > 0 ? size : -size); + BOOST_ASSERT(t > 0 || boost::is_signed::value); - /** - * \brief Save integer types. - * - * First we save the size information ie. the number of bytes that hold the - * actual data. We subsequently transform the data using store_little_endian - * and store non-zero bytes to the stream. - */ - template - typename boost::enable_if >::type - save(const T & t, dummy<2> = 0) - { - if (T temp = t) - { - // examine the number of bytes - // needed to represent the number - signed char size = 0; - do { temp >>= CHAR_BIT; ++size; } while (temp != 0 && temp != (T)-1); - - // encode the sign bit into the size - save_signed_char(t > 0 ? size : -size); - BOOST_ASSERT(t > 0 || boost::is_signed::value); -#if BOOST_VERSION < 105800 - // we choose to use little endian because this way we just - // save the first size bytes to the stream and skip the rest - endian::store_little_endian(&temp, t); -#else - // use new endian library from boost 1.58 - temp = boost::endian::native_to_little(t); -#endif + // we choose to use little endian because this way we just + // save the first size bytes to the stream and skip the rest + temp = boost::endian::native_to_little(t); - save_binary(&temp, size); - } - // zero optimization - else save_signed_char(0); + save_binary(&temp, size); } + // zero optimization + else save_signed_char(0); + } - /** - * \brief Save floating point types. - * - * We simply rely on fp_traits to extract the bit pattern into an (unsigned) - * integral type and store that into the stream. Francois Mauger provided - * standardized behaviour for special values like inf and NaN, that need to - * be serialized in his application. - * - * \note by Johan Rade (author of the floating point utilities library): - * Be warned that the math::detail::fp_traits::type::get_bits() function - * is *not* guaranteed to give you all bits of the floating point number. It - * will give you all bits if and only if there is an integer type that has - * the same size as the floating point you are copying from. It will not - * give you all bits for double if there is no uint64_t. It will not give - * you all bits for long double if sizeof(long double) > 8 or there is no - * uint64_t. - * - * The member fp_traits::type::coverage will tell you whether all bits - * are copied. This is a typedef for either math::detail::all_bits or - * math::detail::not_all_bits. - * - * If the function does not copy all bits, then it will copy the most - * significant bits. So if you serialize and deserialize the way you - * describe, and fp_traits::type::coverage is math::detail::not_all_bits, - * then your floating point numbers will be truncated. This will introduce - * small rounding off errors. - */ - template - typename boost::enable_if >::type - save(const T & t, dummy<3> = 0) + /** + * \brief Save floating point types. + * + * We simply rely on fp_traits to extract the bit pattern into an (unsigned) + * integral type and store that into the stream. Francois Mauger provided + * standardized behaviour for special values like inf and NaN, that need to + * be serialized in his application. + * + * \note by Johan Rade (author of the floating point utilities library): + * Be warned that the math::detail::fp_traits::type::get_bits() function + * is *not* guaranteed to give you all bits of the floating point number. It + * will give you all bits if and only if there is an integer type that has + * the same size as the floating point you are copying from. It will not + * give you all bits for double if there is no uint64_t. It will not give + * you all bits for long double if sizeof(long double) > 8 or there is no + * uint64_t. + * + * The member fp_traits::type::coverage will tell you whether all bits + * are copied. This is a typedef for either math::detail::all_bits or + * math::detail::not_all_bits. + * + * If the function does not copy all bits, then it will copy the most + * significant bits. So if you serialize and deserialize the way you + * describe, and fp_traits::type::coverage is math::detail::not_all_bits, + * then your floating point numbers will be truncated. This will introduce + * small rounding off errors. + */ + template + typename boost::enable_if >::type + save(const T & t, dummy<3> = 0) + { + typedef typename fp::detail::fp_traits::type traits; + + // if the no_infnan flag is set we must throw here + if (get_flags() & no_infnan && !fp::isfinite(t)) + throw portable_archive_exception(t); + + // if you end here there are three possibilities: + // 1. you're serializing a long double which is not portable + // 2. you're serializing a double but have no 64 bit integer + // 3. your machine is using an unknown floating point format + // after reading the note above you still might decide to + // deactivate this static assert and try if it works out. + typename traits::bits bits; + BOOST_STATIC_ASSERT(sizeof(bits) == sizeof(T)); + BOOST_STATIC_ASSERT(std::numeric_limits::is_iec559); + + // examine value closely + switch (fp::fpclassify(t)) { - typedef typename fp::detail::fp_traits::type traits; - - // if the no_infnan flag is set we must throw here - if (get_flags() & no_infnan && !fp::isfinite(t)) - throw portable_archive_exception(t); - - // if you end here there are three possibilities: - // 1. you're serializing a long double which is not portable - // 2. you're serializing a double but have no 64 bit integer - // 3. your machine is using an unknown floating point format - // after reading the note above you still might decide to - // deactivate this static assert and try if it works out. - typename traits::bits bits; - BOOST_STATIC_ASSERT(sizeof(bits) == sizeof(T)); - BOOST_STATIC_ASSERT(std::numeric_limits::is_iec559); - - // examine value closely - switch (fp::fpclassify(t)) - { - //case FP_ZERO: bits = 0; break; - case FP_NAN: bits = traits::exponent | traits::mantissa; break; - case FP_INFINITE: bits = traits::exponent | (t < 0) * traits::sign; break; - case FP_SUBNORMAL: assert(std::numeric_limits::has_denorm); // pass - case FP_ZERO: // note that floats can be ±0.0 - case FP_NORMAL: traits::get_bits(t, bits); break; - default: throw portable_archive_exception(t); - } - - save(bits); + //case FP_ZERO: bits = 0; break; + case FP_NAN: bits = traits::exponent | traits::mantissa; break; + case FP_INFINITE: bits = traits::exponent | (t < 0) * traits::sign; break; + case FP_SUBNORMAL: assert(std::numeric_limits::has_denorm); // pass + case FP_ZERO: // note that floats can be ±0.0 + case FP_NORMAL: traits::get_bits(t, bits); break; + default: throw portable_archive_exception(t); } - // in boost 1.44 version_type was splitted into library_version_type and - // item_version_type, plus a whole bunch of additional strong typedefs. - template - typename boost::disable_if >::type - save(const T& t, dummy<4> = 0) - { - // we provide a generic save routine for all types that feature - // conversion operators into an unsigned integer value like those - // created through BOOST_STRONG_TYPEDEF(X, some unsigned int) like - // library_version_type, collection_size_type, item_version_type, - // class_id_type, object_id_type, version_type and tracking_type - save((typename boost::uint_t::least)(t)); - } - }; + save(bits); + } - // polymorphic portable binary oarchive typedef - typedef POLYMORPHIC(portable_oarchive) polymorphic_portable_oarchive; -#undef POLYMORPHIC + // in boost 1.44 version_type was splitted into library_version_type and + // item_version_type, plus a whole bunch of additional strong typedefs. + template + typename boost::disable_if >::type + save(const T& t, dummy<4> = 0) + { + // we provide a generic save routine for all types that feature + // conversion operators into an unsigned integer value like those + // created through BOOST_STRONG_TYPEDEF(X, some unsigned int) like + // library_version_type, collection_size_type, item_version_type, + // class_id_type, object_id_type, version_type and tracking_type + save((typename boost::uint_t::least)(t)); + } + }; + + // polymorphic portable binary oarchive typedef + typedef detail::polymorphic_oarchive_route polymorphic_portable_oarchive; - } // namespace archive -} // namespace boost +} } // namespace boost::archive // required by export -#if BOOST_VERSION < 103500 -#define BOOST_ARCHIVE_CUSTOM_OARCHIVE_TYPES eos::portable_oarchive -#else BOOST_SERIALIZATION_REGISTER_ARCHIVE(boost::archive::portable_oarchive) BOOST_SERIALIZATION_REGISTER_ARCHIVE(boost::archive::polymorphic_portable_oarchive) -#endif // if you include this header multiple times and your compiler is picky // about multiple template instantiations (eg. gcc is) then you need to @@ -444,9 +378,7 @@ BOOST_SERIALIZATION_REGISTER_ARCHIVE(boost::archive::polymorphic_portable_oarchi #include #include -#if BOOST_VERSION < 104000 -#include -#elif !defined BOOST_ARCHIVE_SERIALIZER_INCLUDED +#ifndef BOOST_ARCHIVE_SERIALIZER_INCLUDED #include #define BOOST_ARCHIVE_SERIALIZER_INCLUDED #endif @@ -458,20 +390,12 @@ namespace boost { namespace archive { template class basic_binary_oprimitive< portable_oarchive - #if BOOST_VERSION < 103400 - , std::ostream - #else , std::ostream::char_type , std::ostream::traits_type - #endif >; -#if BOOST_VERSION < 104000 - template class detail::archive_pointer_oserializer; -#else + // need to instantiate this template also for polymorphic version? template class detail::archive_serializer_map; - //template class detail::archive_serializer_map; -#endif } } // namespace boost::archive From 5608a5caa95549f47ca1eb226938a75eb64ccb5a Mon Sep 17 00:00:00 2001 From: Christian Pfligersdorffer Date: Wed, 24 Feb 2016 22:38:02 +0100 Subject: [PATCH 5/5] Created cpp files for portable archive template instantiations. --- build/Jamfile.v2 | 2 ++ include/boost/archive/portable_iarchive.hpp | 32 ----------------- include/boost/archive/portable_oarchive.hpp | 32 ----------------- src/portable_iarchive.cpp | 40 +++++++++++++++++++++ src/portable_oarchive.cpp | 40 +++++++++++++++++++++ 5 files changed, 82 insertions(+), 64 deletions(-) create mode 100644 src/portable_iarchive.cpp create mode 100644 src/portable_oarchive.cpp diff --git a/build/Jamfile.v2 b/build/Jamfile.v2 index 8fd92e6480..bc4774b179 100644 --- a/build/Jamfile.v2 +++ b/build/Jamfile.v2 @@ -71,6 +71,8 @@ SOURCES = extended_type_info_no_rtti polymorphic_iarchive polymorphic_oarchive + portable_iarchive + portable_oarchive stl_port text_iarchive text_oarchive diff --git a/include/boost/archive/portable_iarchive.hpp b/include/boost/archive/portable_iarchive.hpp index 4c70600add..2fc289fe0d 100755 --- a/include/boost/archive/portable_iarchive.hpp +++ b/include/boost/archive/portable_iarchive.hpp @@ -385,35 +385,3 @@ namespace boost { namespace archive { // classes with all the inbuilt archives plus our archive. BOOST_SERIALIZATION_REGISTER_ARCHIVE(boost::archive::portable_iarchive) BOOST_SERIALIZATION_REGISTER_ARCHIVE(boost::archive::polymorphic_portable_iarchive) - -// if you include this header multiple times and your compiler is picky -// about multiple template instantiations (eg. gcc is) then you need to -// define NO_EXPLICIT_TEMPLATE_INSTANTIATION before every include but one -// or you move the instantiation section into an implementation file -#ifndef NO_EXPLICIT_TEMPLATE_INSTANTIATION - -#include -#include - -#ifndef BOOST_ARCHIVE_SERIALIZER_INCLUDED -#include -#define BOOST_ARCHIVE_SERIALIZER_INCLUDED -#endif - -namespace boost { namespace archive { - - // explicitly instantiate for this type of binary stream - template class basic_binary_iarchive; - - template class basic_binary_iprimitive< - portable_iarchive - , std::istream::char_type - , std::istream::traits_type - >; - - // need to instantiate this template also for polymorphic version? - template class detail::archive_serializer_map; - -} } // namespace boost::archive - -#endif diff --git a/include/boost/archive/portable_oarchive.hpp b/include/boost/archive/portable_oarchive.hpp index b033119ef3..f28b267b12 100755 --- a/include/boost/archive/portable_oarchive.hpp +++ b/include/boost/archive/portable_oarchive.hpp @@ -368,35 +368,3 @@ namespace boost { namespace archive { // required by export BOOST_SERIALIZATION_REGISTER_ARCHIVE(boost::archive::portable_oarchive) BOOST_SERIALIZATION_REGISTER_ARCHIVE(boost::archive::polymorphic_portable_oarchive) - -// if you include this header multiple times and your compiler is picky -// about multiple template instantiations (eg. gcc is) then you need to -// define NO_EXPLICIT_TEMPLATE_INSTANTIATION before every include but one -// or you move the instantiation section into an implementation file -#ifndef NO_EXPLICIT_TEMPLATE_INSTANTIATION - -#include -#include - -#ifndef BOOST_ARCHIVE_SERIALIZER_INCLUDED -#include -#define BOOST_ARCHIVE_SERIALIZER_INCLUDED -#endif - -namespace boost { namespace archive { - - // explicitly instantiate for this type of binary stream - template class basic_binary_oarchive; - - template class basic_binary_oprimitive< - portable_oarchive - , std::ostream::char_type - , std::ostream::traits_type - >; - - // need to instantiate this template also for polymorphic version? - template class detail::archive_serializer_map; - -} } // namespace boost::archive - -#endif diff --git a/src/portable_iarchive.cpp b/src/portable_iarchive.cpp new file mode 100644 index 0000000000..0e3f48c5e0 --- /dev/null +++ b/src/portable_iarchive.cpp @@ -0,0 +1,40 @@ +/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8 +// binary_iarchive.cpp: + +// (C) Copyright 2002 Robert Ramey - http://www.rrsd.com . +// Use, modification and distribution is subject to the Boost Software +// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for updates, documentation, and revision history. + +#include + +#define BOOST_ARCHIVE_SOURCE +#include +#include +#include + +#include +#include +#include + +namespace boost { +namespace archive { + + + // explicitly instantiate for this type of binary stream + template class basic_binary_iarchive; + + template class basic_binary_iprimitive< + portable_iarchive + , std::istream::char_type + , std::istream::traits_type + >; + + // need to instantiate this template also for polymorphic version? + template class detail::archive_serializer_map; + template class detail::archive_serializer_map; + +} // namespace archive +} // namespace boost diff --git a/src/portable_oarchive.cpp b/src/portable_oarchive.cpp new file mode 100644 index 0000000000..29ff0c7408 --- /dev/null +++ b/src/portable_oarchive.cpp @@ -0,0 +1,40 @@ +/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8 +// binary_oarchive.cpp: + +// (C) Copyright 2002 Robert Ramey - http://www.rrsd.com . +// Use, modification and distribution is subject to the Boost Software +// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for updates, documentation, and revision history. + +#include + +#define BOOST_ARCHIVE_SOURCE +#include +#include +#include + +// explicitly instantiate for this type of binary stream +#include +#include +#include + +namespace boost { +namespace archive { + + // explicitly instantiate for this type of binary stream + template class basic_binary_oarchive; + + template class basic_binary_oprimitive< + portable_oarchive + , std::ostream::char_type + , std::ostream::traits_type + >; + + // need to instantiate this template also for polymorphic version? + template class detail::archive_serializer_map; + template class detail::archive_serializer_map; + +} // namespace archive +} // namespace boost