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