![]() |
|
|||
File indexing completed on 2024-04-06 12:02:32
0001 /*****************************************************************************/ 0002 /** 0003 * \file portable_oarchive.hpp 0004 * \brief Provides an archive to create portable binary files. 0005 * \author christian.pfligersdorffer@gmx.at 0006 * \version 5.1 0007 * 0008 * This pair of archives brings the advantages of binary streams to the cross 0009 * platform boost::serialization user. While being almost as fast as the native 0010 * binary archive it allows its files to be exchanged between cpu architectures 0011 * using different byte order (endianness). Speaking of speed: in serializing 0012 * numbers the (portable) binary approach is approximately ten times faster than 0013 * the ascii implementation (that is inherently portable)! 0014 * 0015 * Based on the portable archive example by Robert Ramey this implementation 0016 * uses Beman Dawes endian library and fp_utilities from Johan Rade, both being 0017 * in boost since 1.36. Prior to that you need to add them both (header only) 0018 * to your boost directory before you're able to use the archives provided. 0019 * Our archives have been tested successfully for boost versions 1.33 to 1.49! 0020 * 0021 * \note Correct behaviour has so far been confirmed using PowerPC-32, x86-32 0022 * and x86-64 platforms featuring different byte order. So there is a good 0023 * chance it will instantly work for your specific setup. If you encounter 0024 * problems or have suggestions please contact the author. 0025 * 0026 * \note Version 5.1 is now compatible with boost up to version 1.59. Thanks to 0027 * ecotax for pointing to the issue with shared_ptr_helper. 0028 * 0029 * \note Version 5.0 is now compatible with boost up to version 1.49 and enables 0030 * serialization of std::wstring by converting it to/from utf8 (thanks to 0031 * Arash Abghari for this suggestion). With that all unit tests from the 0032 * serialization library pass again with the notable exception of user 0033 * defined primitive types. Those are not supported and as a result any 0034 * user defined type to be used with the portable archives are required 0035 * to be at least object_serializable. 0036 * 0037 * \note Oliver Putz pointed out that -0.0 was not serialized correctly, so 0038 * version 4.3 provides a fix for that. Thanks Ollie! 0039 * 0040 * \note Version 4.2 maintains compatibility with the latest boost 1.45 and adds 0041 * serialization of special floating point values inf and NaN as proposed 0042 * by Francois Mauger. 0043 * 0044 * \note Version 4.1 makes the archives work together with boost 1.40 and 1.41. 0045 * Thanks to Francois Mauger for his suggestions. 0046 * 0047 * \note Version 4 removes one level of the inheritance hierarchy and directly 0048 * builds upon binary primitive and basic binary archive, thereby fixing 0049 * the last open issue regarding array serialization. Thanks to Robert 0050 * Ramey for the hint. 0051 * 0052 * \note A few fixes introduced in version 3.1 let the archives pass all of the 0053 * serialization tests. Thanks to Sergey Morozov for running the tests. 0054 * Wouter Bijlsma pointed out where to find the fp_utilities and endian 0055 * libraries headers inside the boost distribution. I would never have 0056 * found them so thank him it works out of the box since boost 1.36. 0057 * 0058 * \note With Version 3.0 the archives have been made portable across different 0059 * boost versions. For that purpose a header is added to the data that 0060 * supplies the underlying serialization library version. Backwards 0061 * compatibility is maintained by assuming library version boost 1.33 if 0062 * the iarchive is created using the no_header flag. Whether a header is 0063 * present or not can be guessed by peeking into the stream: the header's 0064 * first byte is the magic number 127 coinciding with 'e'|'o'|'s' :-) 0065 * 0066 * \note Version 2.1 removes several compiler warnings and enhances floating 0067 * point diagnostics to inform the user if some preconditions are violated 0068 * on his platform. We do not strive for the universally portable solution 0069 * in binary floating point serialization as desired by some boost users. 0070 * Instead we support only the most widely used IEEE 754 format and try to 0071 * detect when requirements are not met and hence our approach must fail. 0072 * Contributions we made by Johan Rade and Ákos Maróy. 0073 * 0074 * \note Version 2.0 fixes a serious bug that effectively transformed most 0075 * of negative integral values into positive values! For example the two 0076 * numbers -12 and 234 were stored in the same 8-bit pattern and later 0077 * always restored to 234. This was fixed in this version in a way that 0078 * does not change the interpretation of existing archives that did work 0079 * because there were no negative numbers. The other way round archives 0080 * created by version 2.0 and containing negative numbers will raise an 0081 * integer type size exception when reading it with version 1.0. Thanks 0082 * to Markus Frohnmaier for testing the archives and finding the bug. 0083 * 0084 * \copyright The boost software license applies. 0085 */ 0086 /*****************************************************************************/ 0087 0088 #ifndef CondFormats_Serialization_portable_oarchive_hpp 0089 #define CondFormats_Serialization_portable_oarchive_hpp 0090 0091 #include <ostream> 0092 0093 // basic headers 0094 #include <boost/version.hpp> 0095 #include <boost/utility/enable_if.hpp> 0096 #include <boost/archive/basic_binary_oprimitive.hpp> 0097 #include <boost/archive/basic_binary_oarchive.hpp> 0098 0099 #if BOOST_VERSION >= 103500 && BOOST_VERSION < 105600 0100 #include <boost/archive/shared_ptr_helper.hpp> 0101 #endif 0102 0103 #if BOOST_VERSION >= 104500 0104 #include <boost/program_options/config.hpp> 0105 #include <boost/program_options/detail/convert.hpp> 0106 #endif 0107 0108 // funny polymorphics 0109 #if BOOST_VERSION < 103500 0110 #include <boost/archive/detail/polymorphic_oarchive_impl.hpp> 0111 #define POLYMORPHIC(T) boost::archive::detail::polymorphic_oarchive_impl<T> 0112 0113 #elif BOOST_VERSION < 103600 0114 #include <boost/archive/detail/polymorphic_oarchive_dispatch.hpp> 0115 #define POLYMORPHIC(T) boost::archive::detail::polymorphic_oarchive_dispatch<T> 0116 0117 #else 0118 #include <boost/archive/detail/polymorphic_oarchive_route.hpp> 0119 #define POLYMORPHIC(T) boost::archive::detail::polymorphic_oarchive_route<T> 0120 #endif 0121 0122 // endian and fpclassify 0123 #include <boost/math/special_functions/fpclassify.hpp> 0124 #include <boost/endian/conversion.hpp> 0125 0126 // namespace alias fp_classify 0127 #if BOOST_VERSION < 103800 0128 namespace fp = boost::math; 0129 #elif BOOST_VERSION >= 106900 0130 namespace fp = boost::math; 0131 #else 0132 namespace fp = boost::spirit::math; 0133 #endif 0134 0135 #if BOOST_VERSION >= 104500 && !defined BOOST_NO_STD_WSTRING 0136 // used for wstring to utf8 conversion 0137 #include <boost/program_options/config.hpp> 0138 #include <boost/program_options/detail/convert.hpp> 0139 #endif 0140 0141 // generic type traits for numeric types 0142 #include <boost/type_traits/is_integral.hpp> 0143 #include <boost/type_traits/is_signed.hpp> 0144 #include <boost/type_traits/is_arithmetic.hpp> 0145 #include <boost/type_traits/is_floating_point.hpp> 0146 0147 #include "portable_archive_exception.hpp" 0148 0149 // hint from Johan Rade: on VMS there is still support for 0150 // the VAX floating point format and this macro detects it 0151 #if defined(__vms) && defined(__DECCXX) && !__IEEE_FLOAT 0152 #error "VAX floating point format is not supported!" 0153 #endif 0154 0155 namespace eos { 0156 0157 // forward declaration 0158 class portable_oarchive; 0159 0160 typedef boost::archive::basic_binary_oprimitive<portable_oarchive 0161 #if BOOST_VERSION < 103400 0162 , 0163 std::ostream 0164 #else 0165 , 0166 std::ostream::char_type, 0167 std::ostream::traits_type 0168 #endif 0169 > 0170 portable_oprimitive; 0171 0172 /** 0173 * \brief Portable binary output archive using little endian format. 0174 * 0175 * This archive addresses integer size, endianness and floating point types so 0176 * that data can be transferred across different systems. The archive consists 0177 * primarily of three different save implementations for integral types, 0178 * floating point types and string types. Those functions are templates and use 0179 * enable_if to be correctly selected for overloading. 0180 * 0181 * \note The class is based on the portable binary example by Robert Ramey and 0182 * uses Beman Dawes endian library plus fp_utilities by Johan Rade. 0183 */ 0184 class portable_oarchive : public portable_oprimitive 0185 0186 // the example derives from common_oarchive but that lacks the 0187 // save_override functions so we chose to stay one level higher 0188 , 0189 public boost::archive::basic_binary_oarchive<portable_oarchive> 0190 #if BOOST_VERSION >= 103500 && BOOST_VERSION < 105600 0191 // mix-in helper class for serializing shared_ptr 0192 , 0193 public boost::archive::detail::shared_ptr_helper 0194 #endif 0195 { 0196 // workaround for gcc: use a dummy struct 0197 // as additional argument type for overloading 0198 template <int> 0199 struct dummy { 0200 dummy(int) {} 0201 }; 0202 0203 // stores a signed char directly to stream 0204 inline void save_signed_char(const signed char& c) { portable_oprimitive::save(c); } 0205 0206 // archive initialization 0207 void init(unsigned flags) { 0208 // it is vital to have version information if the archive is 0209 // to be parsed with a newer version of boost::serialization 0210 // therefor we create a header, no header means boost 1.33 0211 if (flags & boost::archive::no_header) 0212 BOOST_ASSERT(archive_version == 3); 0213 else { 0214 // write our minimalistic header (magic byte plus version) 0215 // the boost archives write a string instead - by calling 0216 // boost::archive::basic_binary_oarchive<derived_t>::init() 0217 save_signed_char(magic_byte); 0218 0219 // write current version 0220 // save<unsigned>(archive_version); 0221 operator<<(archive_version); 0222 } 0223 } 0224 0225 public: 0226 /** 0227 * \brief Constructor on a stream using ios::binary mode! 0228 * 0229 * We cannot call basic_binary_oprimitive::init which stores type 0230 * sizes to the archive in order to detect transfers to non-compatible 0231 * platforms. 0232 * 0233 * We could have called basic_binary_oarchive::init which would create 0234 * the boost::serialization standard archive header containing also the 0235 * library version. Due to efficiency we stick with our own. 0236 */ 0237 portable_oarchive(std::ostream& os, unsigned flags = 0) 0238 #if BOOST_VERSION < 103400 0239 : portable_oprimitive(os, flags & boost::archive::no_codecvt) 0240 #else 0241 : portable_oprimitive(*os.rdbuf(), flags & boost::archive::no_codecvt) 0242 #endif 0243 , 0244 boost::archive::basic_binary_oarchive<portable_oarchive>(flags) { 0245 init(flags); 0246 } 0247 0248 #if BOOST_VERSION >= 103400 0249 portable_oarchive(std::streambuf& sb, unsigned flags = 0) 0250 : portable_oprimitive(sb, flags & boost::archive::no_codecvt), 0251 boost::archive::basic_binary_oarchive<portable_oarchive>(flags) { 0252 init(flags); 0253 } 0254 #endif 0255 0256 //! Save narrow strings. 0257 void save(const std::string& s) { portable_oprimitive::save(s); } 0258 0259 #ifndef BOOST_NO_STD_WSTRING 0260 /** 0261 * \brief Save wide strings. 0262 * 0263 * This is rather tricky to get right for true portability as there 0264 * are so many different character encodings around. However, wide 0265 * strings that are encoded in one of the Unicode schemes only need 0266 * to be _transcoded_ which is a lot easier actually. 0267 * 0268 * We expect the input string to be encoded in the system's native 0269 * format, ie. UTF-16 on Windows and UTF-32 on Linux machines. Don't 0270 * know about Mac here so I can't really say about that. 0271 */ 0272 void save(const std::wstring& s) { save(boost::to_utf8(s)); } 0273 #endif 0274 0275 /** 0276 * \brief Saving bool type. 0277 * 0278 * Saving bool directly, not by const reference 0279 * because of tracking_type's operator (bool). 0280 * 0281 * \note If you cannot compile your application and it says something 0282 * about save(bool) cannot convert your type const A& into bool then 0283 * you should check your BOOST_CLASS_IMPLEMENTATION setting for A, as 0284 * portable_archive is not able to handle custom primitive types in 0285 * a general manner. 0286 */ 0287 void save(const bool& b) { 0288 save_signed_char(b); 0289 if (b) 0290 save_signed_char('T'); 0291 } 0292 0293 /** 0294 * \brief Save integer types. 0295 * 0296 * First we save the size information ie. the number of bytes that hold the 0297 * actual data. We subsequently transform the data using store_little_endian 0298 * and store non-zero bytes to the stream. 0299 */ 0300 template <typename T> 0301 typename boost::enable_if<boost::is_integral<T> >::type save(const T& t, dummy<2> = 0) { 0302 if (T temp = t) { 0303 // examine the number of bytes 0304 // needed to represent the number 0305 signed char size = 0; 0306 if (sizeof(T) == 1) { 0307 size = 1; 0308 } else { 0309 do { 0310 temp >>= CHAR_BIT; 0311 ++size; 0312 } while (temp != 0 && temp != (T)-1); 0313 } 0314 0315 // encode the sign bit into the size 0316 save_signed_char(t > 0 ? size : -size); 0317 BOOST_ASSERT(t > 0 || boost::is_signed<T>::value); 0318 0319 // we choose to use little endian because this way we just 0320 // save the first size bytes to the stream and skip the rest 0321 temp = boost::endian::native_to_little(t); 0322 save_binary(&temp, size); 0323 } 0324 // zero optimization 0325 else 0326 save_signed_char(0); 0327 } 0328 0329 /** 0330 * \brief Save floating point types. 0331 * 0332 * We simply rely on fp_traits_non_native to extract the bit pattern into an (unsigned) 0333 * integral type and store that into the stream. Francois Mauger provided 0334 * standardized behaviour for special values like inf and NaN, that need to 0335 * be serialized in his application. 0336 * 0337 * \note by Johan Rade (author of the floating point utilities library): 0338 * Be warned that the math::detail::fp_traits_non_native<T,U>::get_bits() function 0339 * is *not* guaranteed to give you all bits of the floating point number. It 0340 * will give you all bits if and only if there is an integer type that has 0341 * the same size as the floating point you are copying from. It will not 0342 * give you all bits for double if there is no uint64_t. It will not give 0343 * you all bits for long double if sizeof(long double) > 8 or there is no 0344 * uint64_t. 0345 * 0346 * The member fp_traits<T>::type::coverage will tell you whether all bits 0347 * are copied. This is a typedef for either math::detail::all_bits or 0348 * math::detail::not_all_bits. 0349 * 0350 * If the function does not copy all bits, then it will copy the most 0351 * significant bits. So if you serialize and deserialize the way you 0352 * describe, and fp_traits<T>::type::coverage is math::detail::not_all_bits, 0353 * then your floating point numbers will be truncated. This will introduce 0354 * small rounding off errors. 0355 */ 0356 template <typename T> 0357 typename boost::enable_if<boost::is_floating_point<T> >::type save(const T& t, dummy<3> = 0) { 0358 typedef typename fp::detail::size_to_precision<sizeof(T), ::std::is_floating_point<T>::value>::type precision; 0359 typedef typename fp::detail::fp_traits_non_native<T, precision> traits; 0360 0361 // if the no_infnan flag is set we must throw here 0362 if (get_flags() & no_infnan && !fp::isfinite(t)) 0363 throw portable_archive_exception(t); 0364 0365 // if you end here there are three possibilities: 0366 // 1. you're serializing a long double which is not portable 0367 // 2. you're serializing a double but have no 64 bit integer 0368 // 3. your machine is using an unknown floating point format 0369 // after reading the note above you still might decide to 0370 // deactivate this static assert and try if it works out. 0371 typename traits::bits bits; 0372 static_assert(sizeof(bits) == sizeof(T)); 0373 static_assert(std::numeric_limits<T>::is_iec559); 0374 0375 // examine value closely 0376 switch (fp::fpclassify(t)) { 0377 //case FP_ZERO: bits = 0; break; 0378 #if BOOST_VERSION >= 106900 0379 case FP_NAN: 0380 bits = traits::exponent | traits::significand; 0381 break; 0382 #else 0383 case FP_NAN: 0384 bits = traits::exponent | traits::mantissa; 0385 break; 0386 #endif 0387 case FP_INFINITE: 0388 bits = traits::exponent | (t < 0) * traits::sign; 0389 break; 0390 case FP_SUBNORMAL: 0391 assert(std::numeric_limits<T>::has_denorm); // pass 0392 case FP_ZERO: // note that floats can be ±0.0 0393 case FP_NORMAL: 0394 traits::get_bits(t, bits); 0395 break; 0396 default: 0397 throw portable_archive_exception(t); 0398 } 0399 0400 save(bits); 0401 } 0402 0403 // in boost 1.44 version_type was splitted into library_version_type and 0404 // item_version_type, plus a whole bunch of additional strong typedefs. 0405 template <typename T> 0406 typename boost::disable_if<boost::is_arithmetic<T> >::type save(const T& t, dummy<4> = 0) { 0407 // we provide a generic save routine for all types that feature 0408 // conversion operators into an unsigned integer value like those 0409 // created through BOOST_STRONG_TYPEDEF(X, some unsigned int) like 0410 // library_version_type, collection_size_type, item_version_type, 0411 // class_id_type, object_id_type, version_type and tracking_type 0412 save((typename boost::uint_t<sizeof(T) * CHAR_BIT>::least)(t)); 0413 } 0414 }; 0415 0416 // polymorphic portable binary oarchive typedef 0417 typedef POLYMORPHIC(portable_oarchive) polymorphic_portable_oarchive; 0418 #undef POLYMORPHIC 0419 0420 } // namespace eos 0421 0422 // required by export 0423 #if BOOST_VERSION < 103500 0424 #define BOOST_ARCHIVE_CUSTOM_OARCHIVE_TYPES eos::portable_oarchive 0425 #else 0426 BOOST_SERIALIZATION_REGISTER_ARCHIVE(eos::portable_oarchive) 0427 BOOST_SERIALIZATION_REGISTER_ARCHIVE(eos::polymorphic_portable_oarchive) 0428 #endif 0429 0430 // if you include this header multiple times and your compiler is picky 0431 // about multiple template instantiations (eg. gcc is) then you need to 0432 // define NO_EXPLICIT_TEMPLATE_INSTANTIATION before every include but one 0433 // or you move the instantiation section into an implementation file 0434 // #ifndef NO_EXPLICIT_TEMPLATE_INSTANTIATION 0435 // 0436 // #include <boost/archive/impl/basic_binary_oarchive.ipp> 0437 // #include <boost/archive/impl/basic_binary_oprimitive.ipp> 0438 // 0439 // #if BOOST_VERSION < 104000 0440 // #include <boost/archive/impl/archive_pointer_oserializer.ipp> 0441 // #elif !defined BOOST_ARCHIVE_SERIALIZER_INCLUDED 0442 // #include <boost/archive/impl/archive_serializer_map.ipp> 0443 // #define BOOST_ARCHIVE_SERIALIZER_INCLUDED 0444 // #endif 0445 // 0446 // namespace boost { 0447 // namespace archive { 0448 // 0449 // // explicitly instantiate for this type of binary stream 0450 // template class basic_binary_oarchive<eos::portable_oarchive>; 0451 // 0452 // template class basic_binary_oprimitive<eos::portable_oarchive 0453 // #if BOOST_VERSION < 103400 0454 // , 0455 // std::ostream 0456 // #else 0457 // , 0458 // std::ostream::char_type, 0459 // std::ostream::traits_type 0460 // #endif 0461 // >; 0462 // 0463 // #if BOOST_VERSION < 104000 0464 // template class detail::archive_pointer_oserializer<eos::portable_oarchive>; 0465 // #else 0466 // template class detail::archive_serializer_map<eos::portable_oarchive>; 0467 // //template class detail::archive_serializer_map<eos::polymorphic_portable_oarchive>; 0468 // #endif 0469 // 0470 // } // namespace archive 0471 // } // namespace boost 0472 // 0473 // #endif 0474 0475 #endif
[ Source navigation ] | [ Diff markup ] | [ Identifier search ] | [ general search ] |
This page was automatically generated by the 2.2.1 LXR engine. The LXR team |
![]() ![]() |