QxOrm
1.5.0
C++ Object Relational Mapping library
|
00001 /*****************************************************************************/ 00086 /*****************************************************************************/ 00087 00088 #ifdef _QX_ENABLE_BOOST_SERIALIZATION 00089 #if _QX_SERIALIZE_PORTABLE_BINARY 00090 #ifndef _QX_PORTABLE_BINARY_OARCHIVE_H_ 00091 #define _QX_PORTABLE_BINARY_OARCHIVE_H_ 00092 00093 #ifdef _MSC_VER 00094 #pragma once 00095 #endif // _MSC_VER 00096 00097 #ifdef _MSC_VER 00098 #pragma warning(push) 00099 #pragma warning(disable:4996) 00100 #pragma warning(disable:4661) 00101 #endif // _MSC_VER 00102 00103 #include <ostream> 00104 00105 // basic headers 00106 #include <boost/version.hpp> 00107 #include <boost/utility/enable_if.hpp> 00108 #include <boost/archive/basic_binary_oprimitive.hpp> 00109 #include <boost/archive/basic_binary_oarchive.hpp> 00110 00111 #if BOOST_VERSION >= 103500 && BOOST_VERSION < 105600 00112 #include <boost/archive/shared_ptr_helper.hpp> 00113 #endif 00114 00115 // endian and fpclassify 00116 #if BOOST_VERSION < 103600 00117 #include <boost/integer/endian.hpp> 00118 #include <boost/math/fpclassify.hpp> 00119 #elif BOOST_VERSION < 104800 00120 #include <boost/spirit/home/support/detail/integer/endian.hpp> 00121 #include <boost/spirit/home/support/detail/math/fpclassify.hpp> 00122 #else 00123 #include <boost/spirit/home/support/detail/endian/endian.hpp> 00124 #include <boost/spirit/home/support/detail/math/fpclassify.hpp> 00125 #endif 00126 00127 // namespace alias fp_classify 00128 #if BOOST_VERSION < 103800 00129 namespace fp = boost::math; 00130 #else 00131 namespace fp = boost::spirit::math; 00132 #endif 00133 00134 // namespace alias endian 00135 #if BOOST_VERSION < 104800 00136 namespace endian = boost::detail; 00137 #else 00138 namespace endian = boost::spirit::detail; 00139 #endif 00140 00141 #if BOOST_VERSION >= 104500 && !defined BOOST_NO_STD_WSTRING 00142 // used for wstring to utf8 conversion 00143 #include <boost/program_options/config.hpp> 00144 #include <boost/program_options/detail/convert.hpp> 00145 #endif 00146 00147 // generic type traits for numeric types 00148 #include <boost/type_traits/is_integral.hpp> 00149 #include <boost/type_traits/is_signed.hpp> 00150 #include <boost/type_traits/is_arithmetic.hpp> 00151 #include <boost/type_traits/is_floating_point.hpp> 00152 00153 #include "portable_archive_exception.hpp" 00154 00155 // hint from Johan Rade: on VMS there is still support for 00156 // the VAX floating point format and this macro detects it 00157 #if defined(__vms) && defined(__DECCXX) && !__IEEE_FLOAT 00158 #error "VAX floating point format is not supported!" 00159 #endif 00160 00161 namespace eos { 00162 00163 // forward declaration 00164 class portable_oarchive; 00165 00166 typedef boost::archive::basic_binary_oprimitive< 00167 portable_oarchive 00168 #if BOOST_VERSION < 103400 00169 , std::ostream 00170 #else 00171 , std::ostream::char_type 00172 , std::ostream::traits_type 00173 #endif 00174 > portable_oprimitive; 00175 00188 class portable_oarchive : public portable_oprimitive 00189 00190 // the example derives from common_oarchive but that lacks the 00191 // save_override functions so we chose to stay one level higher 00192 , public boost::archive::basic_binary_oarchive<portable_oarchive> 00193 00194 #if BOOST_VERSION >= 103500 && BOOST_VERSION < 105600 00195 // mix-in helper class for serializing shared_ptr 00196 , public boost::archive::detail::shared_ptr_helper 00197 #endif 00198 { 00199 // workaround for gcc: use a dummy struct 00200 // as additional argument type for overloading 00201 template<int> struct dummy { dummy(int) {}}; 00202 00203 // stores a signed char directly to stream 00204 inline void save_signed_char(const signed char& c) 00205 { 00206 portable_oprimitive::save(c); 00207 } 00208 00209 // archive initialization 00210 void init(unsigned flags) 00211 { 00212 // it is vital to have version information if the archive is 00213 // to be parsed with a newer version of boost::serialization 00214 // therefor we create a header, no header means boost 1.33 00215 if (flags & boost::archive::no_header) 00216 BOOST_ASSERT(archive_version == 3); 00217 else 00218 { 00219 // write our minimalistic header (magic byte plus version) 00220 // the boost archives write a string instead - by calling 00221 // boost::archive::basic_binary_oarchive<derived_t>::init() 00222 save_signed_char(magic_byte); 00223 00224 // write current version 00225 // save<unsigned>(archive_version); 00226 operator<<(archive_version); 00227 } 00228 } 00229 00230 public: 00242 portable_oarchive(std::ostream& os, unsigned flags = 0) 00243 #if BOOST_VERSION < 103400 00244 : portable_oprimitive(os, flags & boost::archive::no_codecvt) 00245 #else 00246 : portable_oprimitive(*os.rdbuf(), flags & boost::archive::no_codecvt) 00247 #endif 00248 , boost::archive::basic_binary_oarchive<portable_oarchive>(flags) 00249 { 00250 init(flags); 00251 } 00252 00253 #if BOOST_VERSION >= 103400 00254 portable_oarchive(std::streambuf& sb, unsigned flags = 0) 00255 : portable_oprimitive(sb, flags & boost::archive::no_codecvt) 00256 , boost::archive::basic_binary_oarchive<portable_oarchive>(flags) 00257 { 00258 init(flags); 00259 } 00260 #endif 00261 00263 void save(const std::string& s) 00264 { 00265 portable_oprimitive::save(s); 00266 } 00267 00268 #ifndef BOOST_NO_STD_WSTRING 00269 00281 void save(const std::wstring& s) 00282 { 00283 save(boost::to_utf8(s)); 00284 } 00285 #endif 00286 00299 void save(const bool& b) 00300 { 00301 save_signed_char(b); 00302 if (b) save_signed_char('T'); 00303 } 00304 00312 template <typename T> 00313 typename boost::enable_if<boost::is_integral<T> >::type 00314 save(const T & t, dummy<2> = 0) 00315 { 00316 if (T temp = t) 00317 { 00318 // examine the number of bytes 00319 // needed to represent the number 00320 signed char size = 0; 00321 do { temp >>= CHAR_BIT; ++size; } 00322 while (temp != 0 && temp != (T) -1); 00323 00324 // encode the sign bit into the size 00325 save_signed_char(t > 0 ? size : -size); 00326 BOOST_ASSERT(t > 0 || boost::is_signed<T>::value); 00327 00328 // we choose to use little endian because this way we just 00329 // save the first size bytes to the stream and skip the rest 00330 endian::store_little_endian<T, sizeof(T)>(&temp, t); 00331 00332 save_binary(&temp, size); 00333 } 00334 // zero optimization 00335 else save_signed_char(0); 00336 } 00337 00365 template <typename T> 00366 typename boost::enable_if<boost::is_floating_point<T> >::type 00367 save(const T & t, dummy<3> = 0) 00368 { 00369 typedef typename fp::detail::fp_traits<T>::type traits; 00370 00371 // if the no_infnan flag is set we must throw here 00372 if (get_flags() & no_infnan && !fp::isfinite(t)) 00373 throw portable_archive_exception(t); 00374 00375 // if you end here there are three possibilities: 00376 // 1. you're serializing a long double which is not portable 00377 // 2. you're serializing a double but have no 64 bit integer 00378 // 3. your machine is using an unknown floating point format 00379 // after reading the note above you still might decide to 00380 // deactivate this static assert and try if it works out. 00381 typename traits::bits bits; 00382 BOOST_STATIC_ASSERT(sizeof(bits) == sizeof(T)); 00383 BOOST_STATIC_ASSERT(std::numeric_limits<T>::is_iec559); 00384 00385 // examine value closely 00386 switch (fp::fpclassify(t)) 00387 { 00388 //case FP_ZERO: bits = 0; break; 00389 case FP_NAN: bits = traits::exponent | traits::mantissa; break; 00390 case FP_INFINITE: bits = traits::exponent | (t<0) * traits::sign; break; 00391 case FP_SUBNORMAL: assert(std::numeric_limits<T>::has_denorm); // pass 00392 case FP_ZERO: // note that floats can be ±0.0 00393 case FP_NORMAL: traits::get_bits(t, bits); break; 00394 default: throw portable_archive_exception(t); 00395 } 00396 00397 save(bits); 00398 } 00399 00400 // in boost 1.44 version_type was splitted into library_version_type and 00401 // item_version_type, plus a whole bunch of additional strong typedefs. 00402 template <typename T> 00403 typename boost::disable_if<boost::is_arithmetic<T> >::type 00404 save(const T& t, dummy<4> = 0) 00405 { 00406 // we provide a generic save routine for all types that feature 00407 // conversion operators into an unsigned integer value like those 00408 // created through BOOST_STRONG_TYPEDEF(X, some unsigned int) like 00409 // library_version_type, collection_size_type, item_version_type, 00410 // class_id_type, object_id_type, version_type and tracking_type 00411 save((typename boost::uint_t<sizeof(T)*CHAR_BIT>::least)(t)); 00412 } 00413 }; 00414 00415 } // namespace eos 00416 00417 // required by export 00418 #if BOOST_VERSION < 103500 00419 #define BOOST_ARCHIVE_CUSTOM_OARCHIVE_TYPES eos::portable_oarchive 00420 #else 00421 BOOST_SERIALIZATION_REGISTER_ARCHIVE(eos::portable_oarchive) 00422 #endif 00423 00424 // if you include this header multiple times and your compiler is picky 00425 // about multiple template instantiations (eg. gcc is) then you need to 00426 // define NO_EXPLICIT_TEMPLATE_INSTANTIATION before every include but one 00427 // or you move the instantiation section into an implementation file 00428 #ifndef NO_EXPLICIT_TEMPLATE_INSTANTIATION 00429 00430 #ifndef _QX_BOOST_ARCHIVE_SERIALIZER_IMPL_DEFINED_ 00431 #define _QX_BOOST_ARCHIVE_SERIALIZER_IMPL_DEFINED_ 00432 00433 #include <boost/archive/impl/basic_binary_iarchive.ipp> 00434 #include <boost/archive/impl/basic_binary_oarchive.ipp> 00435 #include <boost/archive/impl/basic_binary_iprimitive.ipp> 00436 #include <boost/archive/impl/basic_binary_oprimitive.ipp> 00437 00438 #if _QX_SERIALIZE_TEXT 00439 #include <boost/archive/impl/basic_text_oprimitive.ipp> 00440 #include <boost/archive/impl/basic_text_iprimitive.ipp> 00441 #include <boost/archive/impl/basic_text_oarchive.ipp> 00442 #include <boost/archive/impl/basic_text_iarchive.ipp> 00443 #endif // _QX_SERIALIZE_TEXT 00444 00445 #if (BOOST_VERSION < 104000) 00446 #include <boost/archive/impl/archive_pointer_iserializer.ipp> 00447 #include <boost/archive/impl/archive_pointer_oserializer.ipp> 00448 #elif !defined BOOST_ARCHIVE_SERIALIZER_INCLUDED 00449 #include <boost/archive/impl/archive_serializer_map.ipp> 00450 #define BOOST_ARCHIVE_SERIALIZER_INCLUDED 00451 #endif // (BOOST_VERSION < 104000) 00452 00453 #endif // _QX_BOOST_ARCHIVE_SERIALIZER_IMPL_DEFINED_ 00454 00455 namespace boost { namespace archive { 00456 00457 // explicitly instantiate for this type of binary stream 00458 template class basic_binary_oarchive<eos::portable_oarchive>; 00459 00460 template class basic_binary_oprimitive< 00461 eos::portable_oarchive 00462 #if BOOST_VERSION < 103400 00463 , std::ostream 00464 #else 00465 , std::ostream::char_type 00466 , std::ostream::traits_type 00467 #endif 00468 >; 00469 00470 #if (! _QX_USE_EXPORT_DLL_BOOST_SERIALIZATION_SINGLETON) 00471 #if (BOOST_VERSION < 104000) 00472 template class detail::archive_pointer_oserializer<eos::portable_oarchive>; 00473 #else // (BOOST_VERSION < 104000) 00474 template class detail::archive_serializer_map<eos::portable_oarchive>; 00475 #endif // (BOOST_VERSION < 104000) 00476 #endif // (! _QX_USE_EXPORT_DLL_BOOST_SERIALIZATION_SINGLETON) 00477 00478 } } // namespace boost::archive 00479 00480 #endif // NO_EXPLICIT_TEMPLATE_INSTANTIATION 00481 00482 #ifdef _MSC_VER 00483 #pragma warning(pop) 00484 #endif // _MSC_VER 00485 00486 #endif // _QX_PORTABLE_BINARY_OARCHIVE_H_ 00487 #endif // _QX_SERIALIZE_PORTABLE_BINARY 00488 #endif // _QX_ENABLE_BOOST_SERIALIZATION