QxOrm  1.4.2
C++ Object Relational Mapping library
portable_iarchive.hpp
Go to the documentation of this file.
00001 /*****************************************************************************/
00060 /*****************************************************************************/
00061 
00062 #ifdef _QX_ENABLE_BOOST_SERIALIZATION
00063 #if _QX_SERIALIZE_PORTABLE_BINARY
00064 #ifndef _QX_PORTABLE_BINARY_IARCHIVE_H_
00065 #define _QX_PORTABLE_BINARY_IARCHIVE_H_
00066 
00067 #ifdef _MSC_VER
00068 #pragma once
00069 #endif // _MSC_VER
00070 
00071 #ifdef _MSC_VER
00072 #pragma warning(push)
00073 #pragma warning(disable:4996)
00074 #pragma warning(disable:4661)
00075 #endif // _MSC_VER
00076 
00077 #include <istream>
00078 
00079 // basic headers
00080 #include <boost/version.hpp>
00081 #include <boost/utility/enable_if.hpp>
00082 #include <boost/archive/binary_iarchive.hpp>
00083 
00084 // endian and fpclassify
00085 #if BOOST_VERSION < 103600
00086 #include <boost/integer/endian.hpp>
00087 #include <boost/math/fpclassify.hpp>
00088 #else
00089 #include <boost/spirit/home/support/detail/integer/endian.hpp>
00090 #include <boost/spirit/home/support/detail/math/fpclassify.hpp>
00091 #endif
00092 
00093 // namespace alias
00094 #if BOOST_VERSION < 103800
00095 namespace fp = boost::math;
00096 #else
00097 namespace fp = boost::spirit::math;
00098 #endif
00099 
00100 // generic type traits for numeric types
00101 #include <boost/type_traits/is_integral.hpp>
00102 #include <boost/type_traits/is_unsigned.hpp>
00103 #include <boost/type_traits/is_arithmetic.hpp>
00104 #include <boost/type_traits/is_floating_point.hpp>
00105 
00106 #include "portable_archive_exception.hpp"
00107 
00108 // hint from Johan Rade: on VMS there is still support for
00109 // the VAX floating point format and this macro detects it
00110 #if defined(__vms) && defined(__DECCXX) && !__IEEE_FLOAT
00111 #error "VAX floating point format is not supported!"
00112 #endif
00113 
00114 #if (BOOST_VERSION >= 104400)
00115 #define QX_ARCHIVE_PORTABLE_BINARY_VERSION_PATCH (boost::archive::library_version_type)(3)
00116 #elif (BOOST_VERSION >= 104200)
00117 #define QX_ARCHIVE_PORTABLE_BINARY_VERSION_PATCH (boost::archive::version_type)(3)
00118 #else // (BOOST_VERSION >= 104200)
00119 #define QX_ARCHIVE_PORTABLE_BINARY_VERSION_PATCH 3
00120 #endif // (BOOST_VERSION >= 104200)
00121 
00122 namespace eos {
00123 
00124    // forward declaration
00125    class portable_iarchive;
00126 
00127    // convenience archive base class typedef
00128    typedef boost::archive::binary_iarchive_impl<
00129       portable_iarchive
00130       #if BOOST_VERSION >= 103400
00131          , std::istream::char_type 
00132          , std::istream::traits_type
00133       #endif
00134    > portable_iarchive_base;
00135 
00153    class portable_iarchive : public portable_iarchive_base
00154    #if BOOST_VERSION >= 103500
00155       // mix-in helper class for serializing shared_ptr
00156       , public boost::archive::detail::shared_ptr_helper
00157    #endif
00158    {
00159       // workaround for gcc: use a dummy struct
00160       // as additional argument type for overloading
00161       template <int> struct dummy { dummy(int) {}};
00162 
00163       // loads directly from stream
00164       signed char load_signed_char() 
00165       { 
00166          signed char c; 
00167          portable_iarchive_base::load(c); 
00168          return c; 
00169       }
00170 
00171    public:
00172 
00174       template <class T>
00175       typename boost::disable_if<boost::is_arithmetic<T> >::type 
00176       load(T& t, dummy<1> = 0)
00177       {
00178          portable_iarchive_base::load(t);
00179       }
00180 
00184       void load(bool& b) 
00185       { 
00186          switch (signed char c = load_signed_char())
00187          {
00188             case 0: b = false; break;
00189             case 1: b = load_signed_char(); break;
00190             default: throw portable_archive_exception(c);
00191          }
00192       }
00193 
00201       template <typename T>
00202       typename boost::enable_if<boost::is_integral<T> >::type
00203       load(T & t, dummy<2> = 0)
00204       {
00205          // get the number of bytes in the stream
00206          if (signed char size = load_signed_char())
00207          {
00208             // check for negative value in unsigned type
00209             if (size < 0 && boost::is_unsigned<T>::value) throw portable_archive_exception();
00210 
00211             // check that our type T is large enough
00212             else if ((unsigned) abs(size) > sizeof(T)) throw portable_archive_exception(size);
00213 
00214             // reconstruct the value
00215             T temp = size < 0 ? -1 : 0;
00216             load_binary(&temp, abs(size));
00217 
00218             // load the value from little endian - is is then converted
00219             // to the target type T and fits it because size <= sizeof(T)
00220             t = boost::detail::load_little_endian<T, sizeof(T)>(&temp);
00221          }
00222          else t = 0; // zero optimization
00223       }
00224 
00252       template <typename T>
00253       typename boost::enable_if<boost::is_floating_point<T> >::type
00254       load(T & t, dummy<3> = 0)
00255       {
00256          typedef typename fp::detail::fp_traits<T>::type traits;
00257 
00258          // if you end here there are three possibilities:
00259          // 1. you're serializing a long double which is not portable
00260          // 2. you're serializing a double but have no 64 bit integer
00261          // 3. your machine is using an unknown floating point format
00262          // after reading the note above you still might decide to 
00263          // deactivate this static assert and try if it works out.
00264          typename traits::bits bits;
00265          BOOST_STATIC_ASSERT(sizeof(bits) == sizeof(T));
00266          BOOST_STATIC_ASSERT(std::numeric_limits<T>::is_iec559);
00267 
00268          load(bits);
00269          traits::set_bits(t, bits);
00270 
00271          // if you end here your floating point type does not support 
00272          // denormalized numbers. this might be the case even though 
00273          // your type conforms to IEC 559 (and thus to IEEE 754)
00274          if (std::numeric_limits<T>::has_denorm == std::denorm_absent 
00275          && t && !fp::isnormal(t)) throw portable_archive_exception(t);
00276       }
00277 
00279       portable_iarchive(std::istream& is, unsigned flags = 0)
00280          // the base class constructor calls basic_binary_iarchive::init
00281          // but also basic_binary_iprimitive::init which loads type sizes
00282          // and throws an exception when they are different - no way!
00283          : portable_iarchive_base(is, flags | boost::archive::no_header) 
00284       {
00285          using namespace boost::archive;
00286 
00287          // it is vital to have version information!
00288          // if we don't have any we assume boost 1.33
00289          if (flags & no_header) set_library_version(QX_ARCHIVE_PORTABLE_BINARY_VERSION_PATCH);
00290 
00291          // extract and check the magic eos byte
00292          else if (load_signed_char() != magic_byte) throw archive_exception(archive_exception::invalid_signature);
00293 
00294          else
00295          {
00296             // extract version information
00297             version_type input_library_version;
00298             operator>>(input_library_version);
00299 
00300             // throw if file version is newer than we are
00301             if (input_library_version > archive_version)
00302             throw archive_exception(archive_exception::unsupported_version);
00303 
00304             // else set the library version accordingly
00305             else set_library_version(input_library_version);
00306          }
00307       }
00308    };
00309 
00310 } // namespace eos
00311 
00312 // this is required by export which registers all of your
00313 // classes with all the inbuilt archives plus our archive.
00314 #if BOOST_VERSION < 103500
00315 #define BOOST_ARCHIVE_CUSTOM_IARCHIVE_TYPES eos::portable_iarchive
00316 #else
00317 BOOST_SERIALIZATION_REGISTER_ARCHIVE(eos::portable_iarchive)
00318 #endif
00319 
00320 namespace boost { namespace archive {
00321 
00322    // explicitly instantiate for this type of binary stream
00323    template class basic_binary_iarchive<eos::portable_iarchive>;
00324 
00325    template class basic_binary_iprimitive<
00326       eos::portable_iarchive
00327       #if BOOST_VERSION < 103400
00328          , std::istream
00329       #else
00330          , std::istream::char_type 
00331          , std::istream::traits_type
00332       #endif
00333    >;
00334 
00335    template class binary_iarchive_impl<
00336       eos::portable_iarchive
00337       #if BOOST_VERSION >= 103400
00338          , std::istream::char_type 
00339          , std::istream::traits_type
00340       #endif
00341    >;
00342 
00343 #if (! _QX_USE_EXPORT_DLL_BOOST_SERIALIZATION_SINGLETON)
00344 #if (BOOST_VERSION > 103800)
00345    template class detail::archive_serializer_map<eos::portable_iarchive>;
00346 #else
00347    template class detail::archive_pointer_iserializer<eos::portable_iarchive>;
00348 #endif // (BOOST_VERSION > 103800)
00349 #endif // (! _QX_USE_EXPORT_DLL_BOOST_SERIALIZATION_SINGLETON)
00350 
00351 } } // namespace boost::archive
00352 
00353 #ifdef _MSC_VER
00354 #pragma warning(pop)
00355 #endif // _MSC_VER
00356 
00357 #endif // _QX_PORTABLE_BINARY_IARCHIVE_H_
00358 #endif // _QX_SERIALIZE_PORTABLE_BINARY
00359 #endif // _QX_ENABLE_BOOST_SERIALIZATION