boost/archive/detail/iserializer.hpp
#ifndef BOOST_ARCHIVE_DETAIL_ISERIALIZER_HPP #define BOOST_ARCHIVE_DETAIL_ISERIALIZER_HPP // MS compatible compilers support #pragma once #if defined(_MSC_VER) && (_MSC_VER >= 1020) # pragma once #pragma inline_depth(255) #pragma inline_recursion(on) #endif #if defined(__MWERKS__) #pragma inline_depth(255) #endif /////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8 // iserializer.hpp: interface for serialization system. // (C) Copyright 2002 Robert Ramey - http://www.rrsd.com . // Use, modification and distribution is subject to the Boost Software // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // See http://www.boost.org for updates, documentation, and revision history. #include <new> // for placement new #include <memory> // for auto_ptr #include <cstddef> // size_t #include <boost/config.hpp> #if defined(BOOST_NO_STDC_NAMESPACE) namespace std{ using ::size_t; } // namespace std #endif #include <boost/detail/workaround.hpp> #include <boost/static_assert.hpp> #include <boost/static_warning.hpp> #include <boost/smart_cast.hpp> #include <boost/type_traits/is_pointer.hpp> #include <boost/type_traits/is_fundamental.hpp> #include <boost/type_traits/is_enum.hpp> #include <boost/type_traits/is_same.hpp> #include <boost/type_traits/remove_const.hpp> #include <boost/throw_exception.hpp> #include <boost/serialization/is_abstract.hpp> #include <boost/mpl/eval_if.hpp> #include <boost/mpl/if.hpp> #include <boost/mpl/identity.hpp> #include <boost/mpl/or.hpp> #include <boost/mpl/and.hpp> #include <boost/mpl/less.hpp> #include <boost/mpl/greater_equal.hpp> #include <boost/mpl/int.hpp> #include <boost/mpl/list.hpp> #include <boost/mpl/empty.hpp> #include <boost/mpl/not.hpp> // the following is only used with VC for now and it crashes // at least one other compiler (Borland) #if defined(BOOST_MSVC) #include <boost/mpl/find.hpp> #endif // the following is need only for dynamic cast of polymorphic pointers #include <boost/archive/detail/basic_iarchive.hpp> #include <boost/archive/detail/basic_iserializer.hpp> #include <boost/archive/detail/archive_pointer_iserializer.hpp> #include <boost/serialization/force_include.hpp> #include <boost/serialization/extended_type_info.hpp> #include <boost/serialization/serialization.hpp> #include <boost/serialization/version.hpp> #include <boost/serialization/level.hpp> #include <boost/serialization/tracking.hpp> #include <boost/serialization/type_info_implementation.hpp> #include <boost/serialization/nvp.hpp> #include <boost/serialization/force_include.hpp> #include <boost/archive/archive_exception.hpp> #include <boost/archive/detail/known_archive_types_fwd.hpp> namespace boost { namespace archive { // an accessor to permit friend access to archives. Needed because // some compilers don't handle friend templates completely class load_access { public: template<class Archive, class T> static void load_primitive(Archive &ar, T &t){ ar.load(t); } }; namespace detail { template<class Archive, class T> class iserializer : public basic_iserializer { private: virtual void destroy(/*const*/ void *address) const { boost::serialization::access::destroy(static_cast<T *>(address)); } public: explicit iserializer() : basic_iserializer( * boost::serialization::type_info_implementation<T>::type::get_instance() ) {} virtual BOOST_DLLEXPORT void load_object_data( basic_iarchive & ar, void *x, const unsigned int file_version ) const BOOST_USED ; virtual bool class_info() const { return boost::serialization::implementation_level<T>::value >= boost::serialization::object_class_info; } virtual bool tracking() const { return boost::serialization::tracking_level<T>::value == boost::serialization::track_always || boost::serialization::tracking_level<T>::value == boost::serialization::track_selectivly && serialized_as_pointer(); } virtual unsigned int version() const { return ::boost::serialization::version<T>::value; } virtual bool is_polymorphic() const { typedef BOOST_DEDUCED_TYPENAME boost::serialization::type_info_implementation< T >::type::is_polymorphic::type typex; return typex::value; } static iserializer & instantiate(){ static iserializer instance; return instance; } virtual ~iserializer(){}; }; template<class Archive, class T> BOOST_DLLEXPORT void iserializer<Archive, T>::load_object_data( basic_iarchive & ar, void *x, const unsigned int file_version ) const { // make sure call is routed through the higest interface that might // be specialized by the user. boost::serialization::serialize_adl<Archive, T>( boost::smart_cast_reference<Archive &>(ar), * static_cast<T *>(x), file_version ); } // instantiation of this template creates a static object. Note inversion of // normal argument order to workaround bizarre error in MSVC 6.0 which only // manifests iftself during compiler time. template<class T, class Archive> class pointer_iserializer : public archive_pointer_iserializer<Archive> { private: virtual const basic_iserializer & get_basic_serializer() const { return iserializer<Archive, T>::instantiate(); } virtual BOOST_DLLEXPORT void load_object_ptr( basic_iarchive & ar, void * & x, const unsigned int file_version ) const BOOST_USED; public: static const pointer_iserializer instance; explicit pointer_iserializer() : archive_pointer_iserializer<Archive>( * boost::serialization::type_info_implementation<T>::type::get_instance() ) { basic_iserializer & bis = iserializer<Archive, T>::instantiate(); bis.set_bpis(this); } static BOOST_DLLEXPORT const pointer_iserializer & instantiate() BOOST_USED; virtual ~pointer_iserializer(){}; }; // note: instances of this template to be constructed before the main // is called in order for things to be initialized properly. For this // reason, hiding the instance in a static function as was done above // won't work here so we created a free instance here. template<class T, class Archive> const pointer_iserializer<T, Archive> pointer_iserializer<T, Archive>::instance; // note trick to be sure that operator new is using class specific // version if such exists. Due to Peter Dimov. // note: the following fails if T has no default constructor. // otherwise it would have been ideal //struct heap_allocator : public T //{ // T * invoke(){ // return ::new(sizeof(T)); // } //} // note: this should really be a member of the load_ptr function // below but some compilers still complain about this. template<class T> struct heap_allocator { #if 0 // note: this fails on msvc 7.0 and gcc 3.2 template <class U, U x> struct test; typedef char* yes; typedef int* no; template <class U> yes has_op_new(U*, test<void* (*)(std::size_t), &U::operator new>* = 0); no has_op_new(...); template<class U> T * new_operator(U); T * new_operator(yes){ return (T::operator new)(sizeof(T)); } T * new_operator(no){ return static_cast<T *>(operator new(sizeof(T))); } static T * invoke(){ return new_operator(has_op_new(static_cast<T *>(NULL))); } #else // while this doesn't handle operator new overload for class T static T * invoke(){ return static_cast<T *>(operator new(sizeof(T))); } #endif }; // due to Martin Ecker template <typename T> class auto_ptr_with_deleter { public: explicit auto_ptr_with_deleter(T* p) : m_p(p) {} ~auto_ptr_with_deleter(){ if (m_p) boost::serialization::access::destroy(m_p); } T* get() const { return m_p; } T* release() { T* p = m_p; m_p = NULL; return p; } private: T* m_p; }; template<class Archive, class T> void load_ptr( Archive & ar, T * & t, const BOOST_PFTO unsigned int file_version ){ } template<class T, class Archive> BOOST_DLLEXPORT void pointer_iserializer<T, Archive>::load_object_ptr( basic_iarchive & ar, void * & x, const unsigned int file_version ) const { auto_ptr_with_deleter<T> ap(heap_allocator<T>::invoke()); if(NULL == ap.get()) boost::throw_exception(std::bad_alloc()) ; T * t = ap.get(); x = t; // this addresses an obscure situtation that occurs when load_constructor // de-serializes something through and a pointer. ar.next_object_pointer(t); Archive & ar_impl = boost::smart_cast_reference<Archive &>(ar); boost::serialization::load_construct_data_adl<Archive, T>( ar_impl, t, file_version ); ar_impl >> boost::serialization::make_nvp(NULL, * t); ap.release(); } template<class Archive, class T> struct load_non_pointer_type { // note this bounces the call right back to the archive // with no runtime overhead struct load_primitive { static void invoke(Archive & ar, T & t){ load_access::load_primitive(ar, t); } }; // note this bounces the call right back to the archive // with no runtime overhead struct load_only { static void invoke(Archive & ar, T & t){ // short cut to user's serializer // make sure call is routed through the higest interface that might // be specialized by the user. boost::serialization::serialize_adl( ar, t, boost::serialization::version<T>::value ); } }; // note this save class information including version // and serialization level to the archive struct load { static void invoke(Archive &ar, T &t){ const void * x = &t; ar.load_object(const_cast<void *>(x), iserializer<Archive, T>::instantiate()); } }; static void invoke(Archive & ar, T &t){ BOOST_STATIC_ASSERT(( mpl::greater_equal< boost::serialization::implementation_level<T>, mpl::int_<boost::serialization::primitive_type> >::value )); typedef BOOST_DEDUCED_TYPENAME mpl::eval_if< // if its primitive mpl::equal_to< boost::serialization::implementation_level<T>, mpl::int_<boost::serialization::primitive_type> >, mpl::identity<load_primitive>, // else BOOST_DEDUCED_TYPENAME mpl::eval_if< mpl::and_< // no class info / version mpl::less< boost::serialization::implementation_level<T>, mpl::int_<boost::serialization::object_class_info> >, // and no tracking mpl::equal_to< boost::serialization::tracking_level<T>, mpl::int_<boost::serialization::track_never> > >, // do a fast load mpl::identity<load_only>, // else // do standard load mpl::identity<load> > >::type typex; typex::invoke(ar, t); } }; template<class Archive, class Tptr> struct load_pointer_type { template<class T> struct abstract { static const basic_pointer_iserializer * register_type(Archive & /* ar */){ typedef BOOST_DEDUCED_TYPENAME boost::serialization::type_info_implementation<T>::type::is_polymorphic typex; // it has? to be polymorphic BOOST_STATIC_ASSERT(typex::value); return static_cast<basic_pointer_iserializer *>(NULL); } }; template<class T> struct non_abstract { static const basic_pointer_iserializer * register_type(Archive & ar){ return ar.register_type(static_cast<T *>(NULL)); } }; template<class T> static const basic_pointer_iserializer * register_type(Archive &ar, T & /*t*/){ #if defined(BOOST_MSVC) // note: if you program traps here its because // a) your serializing through a base class pointer // b) to an archive not in the known list. // This will usually occur when one makes a custom archive and // forgets to add it to the list of known archive. If the derived // class is explictly registered or if no derived pointer is used // there won't be a problem - that's why its a warning. However // if you export the derived type and the archive used isn't on the // known list it will fail below at execution time and one will have // a hell of time figuring out why. Hence this warning. BOOST_STATIC_WARNING(( mpl::not_< mpl::and_< BOOST_DEDUCED_TYPENAME serialization::type_info_implementation<T>::type::is_polymorphic, mpl::not_<mpl::empty<known_archive_types<false>::type > >, is_same< mpl::end<known_archive_types<false>::type >::type, BOOST_DEDUCED_TYPENAME mpl::find<known_archive_types<false>::type, Archive>::type > > > ::value )); #endif // there should never be any need to load an abstract polymorphic // class pointer. Inhibiting code generation for this // permits abstract base classes to be used - note: exception // virtual serialize functions used for plug-ins typedef BOOST_DEDUCED_TYPENAME mpl::eval_if< is_abstract<T>, mpl::identity<abstract<T> >, mpl::identity<non_abstract<T> > >::type typex; return typex::register_type(ar); } template<class T> static T * pointer_tweak( const boost::serialization::extended_type_info & eti, void * t, T & ) { // tweak the pointer back to the base class return static_cast<T *>( boost::serialization::void_upcast( eti, * boost::serialization::type_info_implementation<T>::type::get_instance(), t ) ); } static void invoke(Archive & ar, Tptr & t){ const basic_pointer_iserializer * bpis_ptr = register_type(ar, *t); const basic_pointer_iserializer * newbpis_ptr = ar.load_pointer( * reinterpret_cast<void **>(&t), bpis_ptr, archive_pointer_iserializer<Archive>::find ); // if the pointer isn't that of the base class if(newbpis_ptr != bpis_ptr){ t = pointer_tweak(newbpis_ptr->type, t, *t); } } }; template<class Archive, class T> struct load_enum_type { static void invoke(Archive &ar, T &t){ // convert integers to correct enum to load int i; ar >> boost::serialization::make_nvp(NULL, i); t = static_cast<T>(i); } }; template<class Archive, class T> struct load_array_type { static void invoke(Archive &ar, T &t){ // convert integers to correct enum to load int current_count = sizeof(t) / ( static_cast<char *>(static_cast<void *>(&t[1])) - static_cast<char *>(static_cast<void *>(&t[0])) ); int count; ar >> BOOST_SERIALIZATION_NVP(count); if(count > current_count) boost::throw_exception(archive::archive_exception( boost::archive::archive_exception::array_size_too_short )); int i; for(i = 0; i < count; ++i) ar >> boost::serialization::make_nvp("item", t[i]); } }; // note bogus arguments to workaround msvc 6 silent runtime failure template<class Archive, class T> BOOST_DLLEXPORT inline const basic_pointer_iserializer & instantiate_pointer_iserializer( Archive * /* ar = NULL */, T * /* t = NULL */ ) BOOST_USED; template<class Archive, class T> BOOST_DLLEXPORT inline const basic_pointer_iserializer & instantiate_pointer_iserializer( Archive * /* ar = NULL */, T * /* t = NULL */ ){ // note: reversal of order of arguments to work around msvc 6.0 bug // that manifests itself while trying to link. return pointer_iserializer<T, Archive>::instance; } } // detail template<class Archive, class T> inline void load(Archive &ar, T &t){ typedef BOOST_DEDUCED_TYPENAME mpl::eval_if<is_pointer<T>, mpl::identity<detail::load_pointer_type<Archive, T> > ,//else BOOST_DEDUCED_TYPENAME mpl::eval_if<is_array<T>, mpl::identity<detail::load_array_type<Archive, T> > ,//else BOOST_DEDUCED_TYPENAME mpl::eval_if<is_enum<T>, mpl::identity<detail::load_enum_type<Archive, T> > ,//else mpl::identity<detail::load_non_pointer_type<Archive, T> > > > >::type typex; typex::invoke(ar, t); } } // namespace archive } // namespace boost #endif // BOOST_ARCHIVE_DETAIL_ISERIALIZER_HPP