QxOrm  1.5.0
C++ Object Relational Mapping library
portable_iarchive.hpp
Go to the documentation of this file.
00001 /*****************************************************************************/
00083 /*****************************************************************************/
00084 
00085 #ifdef _QX_ENABLE_BOOST_SERIALIZATION
00086 #if _QX_SERIALIZE_PORTABLE_BINARY
00087 #ifndef _QX_PORTABLE_BINARY_IARCHIVE_H_
00088 #define _QX_PORTABLE_BINARY_IARCHIVE_H_
00089 
00090 #ifdef _MSC_VER
00091 #pragma once
00092 #endif // _MSC_VER
00093 
00094 #ifdef _MSC_VER
00095 #pragma warning(push)
00096 #pragma warning(disable:4996)
00097 #pragma warning(disable:4661)
00098 #endif // _MSC_VER
00099 
00100 #include <istream>
00101 
00102 // basic headers
00103 #include <boost/version.hpp>
00104 #include <boost/utility/enable_if.hpp>
00105 #include <boost/archive/basic_binary_iprimitive.hpp>
00106 #include <boost/archive/basic_binary_iarchive.hpp>
00107 
00108 #if BOOST_VERSION >= 103500 && BOOST_VERSION < 105600
00109 #include <boost/archive/shared_ptr_helper.hpp>
00110 #endif
00111 
00112 // endian and fpclassify
00113 #if BOOST_VERSION < 103600
00114 #include <boost/integer/endian.hpp>
00115 #include <boost/math/fpclassify.hpp>
00116 #elif BOOST_VERSION < 104800
00117 #include <boost/spirit/home/support/detail/integer/endian.hpp>
00118 #include <boost/spirit/home/support/detail/math/fpclassify.hpp>
00119 #else
00120 #include <boost/spirit/home/support/detail/endian/endian.hpp>
00121 #include <boost/spirit/home/support/detail/math/fpclassify.hpp>
00122 #endif
00123 
00124 // namespace alias
00125 #if BOOST_VERSION < 103800
00126 namespace fp = boost::math;
00127 #else
00128 namespace fp = boost::spirit::math;
00129 #endif
00130 
00131 // namespace alias endian
00132 #if BOOST_VERSION < 104800
00133 namespace endian = boost::detail;
00134 #else
00135 namespace endian = boost::spirit::detail;
00136 #endif
00137 
00138 #if BOOST_VERSION >= 104500 && !defined BOOST_NO_STD_WSTRING
00139 // used for wstring to utf8 conversion
00140 #include <boost/program_options/config.hpp>
00141 #include <boost/program_options/detail/convert.hpp>
00142 #endif
00143 
00144 // generic type traits for numeric types
00145 #include <boost/type_traits/is_integral.hpp>
00146 #include <boost/type_traits/is_unsigned.hpp>
00147 #include <boost/type_traits/is_arithmetic.hpp>
00148 #include <boost/type_traits/is_floating_point.hpp>
00149 
00150 #include "portable_archive_exception.hpp"
00151 
00152 // hint from Johan Rade: on VMS there is still support for
00153 // the VAX floating point format and this macro detects it
00154 #if defined(__vms) && defined(__DECCXX) && !__IEEE_FLOAT
00155 #error "VAX floating point format is not supported!"
00156 #endif
00157 
00158 namespace eos {
00159 
00160         // forward declaration
00161         class portable_iarchive;
00162 
00163         typedef boost::archive::basic_binary_iprimitive<
00164                 portable_iarchive
00165         #if BOOST_VERSION < 103400
00166                 , std::istream
00167         #else
00168                 , std::istream::char_type 
00169                 , std::istream::traits_type
00170         #endif
00171         > portable_iprimitive;
00172 
00187         class portable_iarchive : public portable_iprimitive
00188 
00189                 // the example derives from common_oarchive but that lacks the
00190                 // load_override functions so we chose to stay one level higher
00191                 , public boost::archive::basic_binary_iarchive<portable_iarchive>
00192 
00193         #if BOOST_VERSION >= 103500 && BOOST_VERSION < 105600
00194                 // mix-in helper class for serializing shared_ptr
00195                 , public boost::archive::detail::shared_ptr_helper
00196         #endif
00197         {
00198                 // only needed for Robert's hack in basic_binary_iarchive::init
00199                 friend class boost::archive::basic_binary_iarchive<portable_iarchive>;
00200 
00201                 // workaround for gcc: use a dummy struct
00202                 // as additional argument type for overloading
00203                 template <int> struct dummy { dummy(int) {}};
00204 
00205                 // loads directly from stream
00206                 inline signed char load_signed_char()
00207                 { 
00208                         signed char c; 
00209                         portable_iprimitive::load(c); 
00210                         return c; 
00211                 }
00212 
00213                 // archive initialization
00214                 void init(unsigned flags)
00215                 {
00216                         using namespace boost::archive;
00217                         archive_version_type input_library_version(3);
00218 
00219                         // it is vital to have version information!
00220                         // if we don't have any we assume boost 1.33
00221                         if (flags & no_header)
00222                                 set_library_version(input_library_version);
00223 
00224                         // extract and check the magic eos byte
00225                         else if (load_signed_char() != magic_byte)
00226                                 throw archive_exception(archive_exception::invalid_signature);
00227 
00228                         else
00229                         {
00230                                 // extract version information
00231                                 operator>>(input_library_version);
00232 
00233                                 // throw if file version is newer than we are
00234                                 if (input_library_version > archive_version)
00235                                         throw archive_exception(archive_exception::unsupported_version);
00236 
00237                                 // else set the library version accordingly
00238                                 else set_library_version(input_library_version);
00239                         }
00240                 }
00241 
00242         public:
00254                 portable_iarchive(std::istream& is, unsigned flags = 0)
00255                 #if BOOST_VERSION < 103400
00256                         : portable_iprimitive(is, flags & boost::archive::no_codecvt)
00257                 #else
00258                         : portable_iprimitive(*is.rdbuf(), flags & boost::archive::no_codecvt)
00259                 #endif
00260                         , boost::archive::basic_binary_iarchive<portable_iarchive>(flags)
00261                 {
00262                         init(flags);
00263                 }
00264 
00265         #if BOOST_VERSION >= 103400
00266                 portable_iarchive(std::streambuf& sb, unsigned flags = 0)
00267                         : portable_iprimitive(sb, flags & boost::archive::no_codecvt)
00268                         , boost::archive::basic_binary_iarchive<portable_iarchive>(flags)
00269                 {
00270                         init(flags);
00271                 }
00272         #endif
00273 
00275                 void load(std::string& s) 
00276                 {
00277                         portable_iprimitive::load(s);
00278                 }
00279 
00280         #ifndef BOOST_NO_STD_WSTRING
00281 
00293                 void load(std::wstring& s)
00294                 {
00295                         std::string utf8;
00296                         load(utf8);
00297                         s = boost::from_utf8(utf8);
00298                 }
00299         #endif
00300 
00314                 void load(bool& b) 
00315                 { 
00316                         switch (signed char c = load_signed_char())
00317                         {
00318                         case 0: b = false; break;
00319                         case 1: b = load_signed_char(); break;
00320                         default: throw portable_archive_exception(c);
00321                         }
00322                 }
00323 
00331                 template <typename T>
00332                 typename boost::enable_if<boost::is_integral<T> >::type
00333                 load(T & t, dummy<2> = 0)
00334                 {
00335                         // get the number of bytes in the stream
00336                         if (signed char size = load_signed_char())
00337                         {
00338                                 // check for negative value in unsigned type
00339                                 if (size < 0 && boost::is_unsigned<T>::value)
00340                                         throw portable_archive_exception();
00341 
00342                                 // check that our type T is large enough
00343                                 else if ((unsigned) abs(size) > sizeof(T)) 
00344                                         throw portable_archive_exception(size);
00345 
00346                                 // reconstruct the value
00347                                 T temp = size < 0 ? -1 : 0;
00348                                 load_binary(&temp, abs(size));
00349 
00350                                 // load the value from little endian - it is then converted
00351                                 // to the target type T and fits it because size <= sizeof(T)
00352                                 t = endian::load_little_endian<T, sizeof(T)>(&temp);
00353                         }
00354 
00355                         else t = 0; // zero optimization
00356                 }
00357 
00385                 template <typename T>
00386                 typename boost::enable_if<boost::is_floating_point<T> >::type
00387                 load(T & t, dummy<3> = 0)
00388                 {
00389                         typedef typename fp::detail::fp_traits<T>::type traits;
00390 
00391                         // if you end here there are three possibilities:
00392                         // 1. you're serializing a long double which is not portable
00393                         // 2. you're serializing a double but have no 64 bit integer
00394                         // 3. your machine is using an unknown floating point format
00395                         // after reading the note above you still might decide to 
00396                         // deactivate this static assert and try if it works out.
00397                         typename traits::bits bits;
00398                         BOOST_STATIC_ASSERT(sizeof(bits) == sizeof(T));
00399                         BOOST_STATIC_ASSERT(std::numeric_limits<T>::is_iec559);
00400 
00401                         load(bits);
00402                         traits::set_bits(t, bits);
00403 
00404                         // if the no_infnan flag is set we must throw here
00405                         if (get_flags() & no_infnan && !fp::isfinite(t))
00406                                 throw portable_archive_exception(t);
00407 
00408                         // if you end here your floating point type does not support 
00409                         // denormalized numbers. this might be the case even though 
00410                         // your type conforms to IEC 559 (and thus to IEEE 754)
00411                         if (std::numeric_limits<T>::has_denorm == std::denorm_absent
00412                                 && fp::fpclassify(t) == (int) FP_SUBNORMAL) // GCC4
00413                                 throw portable_archive_exception(t);
00414                 }
00415 
00416                 // in boost 1.44 version_type was splitted into library_version_type and
00417                 // item_version_type, plus a whole bunch of additional strong typedefs.
00418                 template <typename T>
00419                 typename boost::disable_if<boost::is_arithmetic<T> >::type
00420                 load(T& t, dummy<4> = 0)
00421                 {
00422                         // we provide a generic load routine for all types that feature
00423                         // conversion operators into an unsigned integer value like those
00424                         // created through BOOST_STRONG_TYPEDEF(X, some unsigned int) like
00425                         // library_version_type, collection_size_type, item_version_type,
00426                         // class_id_type, object_id_type, version_type and tracking_type
00427                         load((typename boost::uint_t<sizeof(T)*CHAR_BIT>::least&)(t));
00428                 }
00429         };
00430 
00431 } // namespace eos
00432 
00433 // this is required by export which registers all of your
00434 // classes with all the inbuilt archives plus our archive.
00435 #if BOOST_VERSION < 103500
00436 #define BOOST_ARCHIVE_CUSTOM_IARCHIVE_TYPES eos::portable_iarchive
00437 #else
00438 BOOST_SERIALIZATION_REGISTER_ARCHIVE(eos::portable_iarchive)
00439 #endif
00440 
00441 // if you include this header multiple times and your compiler is picky
00442 // about multiple template instantiations (eg. gcc is) then you need to
00443 // define NO_EXPLICIT_TEMPLATE_INSTANTIATION before every include but one
00444 // or you move the instantiation section into an implementation file
00445 #ifndef NO_EXPLICIT_TEMPLATE_INSTANTIATION
00446 
00447 #ifndef _QX_BOOST_ARCHIVE_SERIALIZER_IMPL_DEFINED_
00448 #define _QX_BOOST_ARCHIVE_SERIALIZER_IMPL_DEFINED_
00449 
00450 #include <boost/archive/impl/basic_binary_iarchive.ipp>
00451 #include <boost/archive/impl/basic_binary_oarchive.ipp>
00452 #include <boost/archive/impl/basic_binary_iprimitive.ipp>
00453 #include <boost/archive/impl/basic_binary_oprimitive.ipp>
00454 
00455 #if _QX_SERIALIZE_TEXT
00456 #include <boost/archive/impl/basic_text_oprimitive.ipp>
00457 #include <boost/archive/impl/basic_text_iprimitive.ipp>
00458 #include <boost/archive/impl/basic_text_oarchive.ipp>
00459 #include <boost/archive/impl/basic_text_iarchive.ipp>
00460 #endif // _QX_SERIALIZE_TEXT
00461 
00462 #if (BOOST_VERSION < 104000)
00463 #include <boost/archive/impl/archive_pointer_iserializer.ipp>
00464 #include <boost/archive/impl/archive_pointer_oserializer.ipp>
00465 #elif !defined BOOST_ARCHIVE_SERIALIZER_INCLUDED
00466 #include <boost/archive/impl/archive_serializer_map.ipp>
00467 #define BOOST_ARCHIVE_SERIALIZER_INCLUDED
00468 #endif // (BOOST_VERSION < 104000)
00469 
00470 #endif // _QX_BOOST_ARCHIVE_SERIALIZER_IMPL_DEFINED_
00471 
00472 namespace boost { namespace archive {
00473 
00474         // explicitly instantiate for this type of binary stream
00475         template class basic_binary_iarchive<eos::portable_iarchive>;
00476 
00477         template class basic_binary_iprimitive<
00478                 eos::portable_iarchive
00479         #if BOOST_VERSION < 103400
00480                 , std::istream
00481         #else
00482                 , std::istream::char_type
00483                 , std::istream::traits_type
00484         #endif
00485         >;
00486 
00487 #if (! _QX_USE_EXPORT_DLL_BOOST_SERIALIZATION_SINGLETON)
00488 #if (BOOST_VERSION < 104000)
00489         template class detail::archive_pointer_iserializer<eos::portable_iarchive>;
00490 #else // (BOOST_VERSION < 104000)
00491         template class detail::archive_serializer_map<eos::portable_iarchive>;
00492 #endif // (BOOST_VERSION < 104000)
00493 #endif // (! _QX_USE_EXPORT_DLL_BOOST_SERIALIZATION_SINGLETON)
00494 
00495 } } // namespace boost::archive
00496 
00497 #endif // NO_EXPLICIT_TEMPLATE_INSTANTIATION
00498 
00499 #ifdef _MSC_VER
00500 #pragma warning(pop)
00501 #endif // _MSC_VER
00502 
00503 #endif // _QX_PORTABLE_BINARY_IARCHIVE_H_
00504 #endif // _QX_SERIALIZE_PORTABLE_BINARY
00505 #endif // _QX_ENABLE_BOOST_SERIALIZATION