QxOrm  1.5.0
C++ Object Relational Mapping library
portable_oarchive.hpp
Go to the documentation of this file.
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