You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and dots ('.'), can be up to 35 characters long. Letters must be lowercase.
632 lines
20 KiB
632 lines
20 KiB
#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(511) |
|
#pragma inline_recursion(on) |
|
#endif |
|
|
|
#if defined(__MWERKS__) |
|
#pragma inline_depth(511) |
|
#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, NULL |
|
|
|
#include <boost/config.hpp> |
|
#include <boost/detail/workaround.hpp> |
|
#if defined(BOOST_NO_STDC_NAMESPACE) |
|
namespace std{ |
|
using ::size_t; |
|
} // namespace std |
|
#endif |
|
|
|
#include <boost/static_assert.hpp> |
|
|
|
#include <boost/mpl/eval_if.hpp> |
|
#include <boost/mpl/identity.hpp> |
|
#include <boost/mpl/greater_equal.hpp> |
|
#include <boost/mpl/equal_to.hpp> |
|
#include <boost/mpl/bool.hpp> |
|
#include <boost/detail/no_exceptions_support.hpp> |
|
|
|
#ifndef BOOST_SERIALIZATION_DEFAULT_TYPE_INFO |
|
#include <boost/serialization/extended_type_info_typeid.hpp> |
|
#endif |
|
#include <boost/serialization/throw_exception.hpp> |
|
#include <boost/serialization/smart_cast.hpp> |
|
#include <boost/serialization/static_warning.hpp> |
|
|
|
#include <boost/type_traits/is_pointer.hpp> |
|
#include <boost/type_traits/is_enum.hpp> |
|
#include <boost/type_traits/is_const.hpp> |
|
#include <boost/type_traits/remove_const.hpp> |
|
#include <boost/type_traits/remove_extent.hpp> |
|
#include <boost/type_traits/is_polymorphic.hpp> |
|
|
|
#include <boost/serialization/assume_abstract.hpp> |
|
|
|
#define DONT_USE_HAS_NEW_OPERATOR ( \ |
|
defined(__BORLANDC__) \ |
|
|| defined(__IBMCPP__) \ |
|
|| defined(BOOST_MSVC) && (BOOST_MSVC <= 1300) \ |
|
|| defined(__SUNPRO_CC) && (__SUNPRO_CC < 0x590) \ |
|
) |
|
|
|
#if ! DONT_USE_HAS_NEW_OPERATOR |
|
#include <boost/type_traits/has_new_operator.hpp> |
|
#endif |
|
|
|
#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/void_cast.hpp> |
|
#include <boost/serialization/array.hpp> |
|
#include <boost/serialization/collection_size_type.hpp> |
|
#include <boost/serialization/singleton.hpp> |
|
#include <boost/serialization/wrapper.hpp> |
|
|
|
// the following is need only for dynamic cast of polymorphic pointers |
|
#include <boost/archive/archive_exception.hpp> |
|
#include <boost/archive/detail/basic_iarchive.hpp> |
|
#include <boost/archive/detail/basic_iserializer.hpp> |
|
#include <boost/archive/detail/basic_pointer_iserializer.hpp> |
|
#include <boost/archive/detail/archive_serializer_map.hpp> |
|
#include <boost/archive/detail/check.hpp> |
|
|
|
namespace boost { |
|
|
|
namespace serialization { |
|
class extended_type_info; |
|
} // namespace serialization |
|
|
|
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 { |
|
|
|
#ifdef BOOST_MSVC |
|
# pragma warning(push) |
|
# pragma warning(disable : 4511 4512) |
|
#endif |
|
|
|
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)); |
|
} |
|
protected: |
|
// protected constructor since it's always created by singleton |
|
explicit iserializer() : |
|
basic_iserializer( |
|
boost::serialization::singleton< |
|
BOOST_DEDUCED_TYPENAME |
|
boost::serialization::type_info_implementation< T >::type |
|
>::get_const_instance() |
|
) |
|
{} |
|
public: |
|
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 unsigned int /* flags */) const { |
|
return boost::serialization::tracking_level< T >::value |
|
== boost::serialization::track_always |
|
|| ( boost::serialization::tracking_level< T >::value |
|
== boost::serialization::track_selectively |
|
&& serialized_as_pointer()); |
|
} |
|
virtual version_type version() const { |
|
return version_type(::boost::serialization::version< T >::value); |
|
} |
|
virtual bool is_polymorphic() const { |
|
return boost::is_polymorphic< T >::value; |
|
} |
|
virtual ~iserializer(){}; |
|
}; |
|
|
|
#ifdef BOOST_MSVC |
|
# pragma warning(pop) |
|
#endif |
|
|
|
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 { |
|
// note: we now comment this out. Before we permited archive |
|
// version # to be very large. Now we don't. To permit |
|
// readers of these old archives, we have to suppress this |
|
// code. Perhaps in the future we might re-enable it but |
|
// permit its suppression with a runtime switch. |
|
#if 0 |
|
// trap case where the program cannot handle the current version |
|
if(file_version > static_cast<const unsigned int>(version())) |
|
boost::serialization::throw_exception( |
|
archive::archive_exception( |
|
boost::archive::archive_exception::unsupported_class_version, |
|
get_debug_info() |
|
) |
|
); |
|
#endif |
|
// make sure call is routed through the higest interface that might |
|
// be specialized by the user. |
|
boost::serialization::serialize_adl( |
|
boost::serialization::smart_cast_reference<Archive &>(ar), |
|
* static_cast<T *>(x), |
|
file_version |
|
); |
|
} |
|
|
|
#ifdef BOOST_MSVC |
|
# pragma warning(push) |
|
# pragma warning(disable : 4511 4512) |
|
#endif |
|
|
|
template<class Archive, class T> |
|
class pointer_iserializer : |
|
public basic_pointer_iserializer |
|
{ |
|
private: |
|
virtual const basic_iserializer & get_basic_serializer() const { |
|
return boost::serialization::singleton< |
|
iserializer<Archive, T> |
|
>::get_const_instance(); |
|
} |
|
BOOST_DLLEXPORT virtual void load_object_ptr( |
|
basic_iarchive & ar, |
|
void * & x, |
|
const unsigned int file_version |
|
) const BOOST_USED; |
|
protected: |
|
// this should alway be a singleton so make the constructor protected |
|
pointer_iserializer(); |
|
~pointer_iserializer(); |
|
}; |
|
|
|
#ifdef BOOST_MSVC |
|
# pragma warning(pop) |
|
#endif |
|
|
|
// 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)); |
|
// } |
|
//} |
|
|
|
template<class T> |
|
struct heap_allocator |
|
{ |
|
// boost::has_new_operator< T > doesn't work on these compilers |
|
#if DONT_USE_HAS_NEW_OPERATOR |
|
// This doesn't handle operator new overload for class T |
|
static T * invoke(){ |
|
return static_cast<T *>(operator new(sizeof(T))); |
|
} |
|
#else |
|
struct has_new_operator { |
|
static T* invoke() { |
|
return static_cast<T *>((T::operator new)(sizeof(T))); |
|
} |
|
}; |
|
struct doesnt_have_new_operator { |
|
static T* invoke() { |
|
return static_cast<T *>(operator new(sizeof(T))); |
|
} |
|
}; |
|
static T * invoke() { |
|
typedef BOOST_DEDUCED_TYPENAME |
|
mpl::eval_if< |
|
boost::has_new_operator< T >, |
|
mpl::identity<has_new_operator >, |
|
mpl::identity<doesnt_have_new_operator > |
|
>::type typex; |
|
return typex::invoke(); |
|
} |
|
#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; |
|
}; |
|
|
|
// note: BOOST_DLLEXPORT is so that code for polymorphic class |
|
// serialized only through base class won't get optimized out |
|
template<class Archive, class T> |
|
BOOST_DLLEXPORT void pointer_iserializer<Archive, T>::load_object_ptr( |
|
basic_iarchive & ar, |
|
void * & x, |
|
const unsigned int file_version |
|
) const |
|
{ |
|
Archive & ar_impl = |
|
boost::serialization::smart_cast_reference<Archive &>(ar); |
|
|
|
auto_ptr_with_deleter< T > ap(heap_allocator< T >::invoke()); |
|
if(NULL == ap.get()) |
|
boost::serialization::throw_exception(std::bad_alloc()) ; |
|
|
|
T * t = ap.get(); |
|
x = t; |
|
|
|
// catch exception during load_construct_data so that we don't |
|
// automatically delete the t which is most likely not fully |
|
// constructed |
|
BOOST_TRY { |
|
// this addresses an obscure situtation that occurs when |
|
// load_constructor de-serializes something through a pointer. |
|
ar.next_object_pointer(t); |
|
boost::serialization::load_construct_data_adl<Archive, T>( |
|
ar_impl, |
|
t, |
|
file_version |
|
); |
|
} |
|
BOOST_CATCH(...){ |
|
ap.release(); |
|
BOOST_RETHROW; |
|
} |
|
BOOST_CATCH_END |
|
|
|
ar_impl >> boost::serialization::make_nvp(NULL, * t); |
|
ap.release(); |
|
} |
|
|
|
template<class Archive, class T> |
|
pointer_iserializer<Archive, T>::pointer_iserializer() : |
|
basic_pointer_iserializer( |
|
boost::serialization::singleton< |
|
BOOST_DEDUCED_TYPENAME |
|
boost::serialization::type_info_implementation< T >::type |
|
>::get_const_instance() |
|
) |
|
{ |
|
boost::serialization::singleton< |
|
iserializer<Archive, T> |
|
>::get_mutable_instance().set_bpis(this); |
|
archive_serializer_map<Archive>::insert(this); |
|
} |
|
|
|
template<class Archive, class T> |
|
pointer_iserializer<Archive, T>::~pointer_iserializer(){ |
|
archive_serializer_map<Archive>::erase(this); |
|
} |
|
|
|
template<class Archive> |
|
struct load_non_pointer_type { |
|
// note this bounces the call right back to the archive |
|
// with no runtime overhead |
|
struct load_primitive { |
|
template<class T> |
|
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 { |
|
template<class T> |
|
static void invoke(Archive & ar, const 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, |
|
const_cast<T &>(t), |
|
boost::serialization::version< T >::value |
|
); |
|
} |
|
}; |
|
|
|
// note this save class information including version |
|
// and serialization level to the archive |
|
struct load_standard { |
|
template<class T> |
|
static void invoke(Archive &ar, const T & t){ |
|
void * x = & const_cast<T &>(t); |
|
ar.load_object( |
|
x, |
|
boost::serialization::singleton< |
|
iserializer<Archive, T> |
|
>::get_const_instance() |
|
); |
|
} |
|
}; |
|
|
|
struct load_conditional { |
|
template<class T> |
|
static void invoke(Archive &ar, T &t){ |
|
//if(0 == (ar.get_flags() & no_tracking)) |
|
load_standard::invoke(ar, t); |
|
//else |
|
// load_only::invoke(ar, t); |
|
} |
|
}; |
|
|
|
template<class T> |
|
static void invoke(Archive & ar, T &t){ |
|
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< |
|
// class info / version |
|
mpl::greater_equal< |
|
boost::serialization::implementation_level< T >, |
|
mpl::int_<boost::serialization::object_class_info> |
|
>, |
|
// do standard load |
|
mpl::identity<load_standard>, |
|
// else |
|
BOOST_DEDUCED_TYPENAME mpl::eval_if< |
|
// 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 a fast load only tracking is turned off |
|
mpl::identity<load_conditional> |
|
> > >::type typex; |
|
check_object_versioning< T >(); |
|
check_object_level< T >(); |
|
typex::invoke(ar, t); |
|
} |
|
}; |
|
|
|
template<class Archive> |
|
struct load_pointer_type { |
|
struct abstract |
|
{ |
|
template<class T> |
|
static const basic_pointer_iserializer * register_type(Archive & /* ar */){ |
|
// it has? to be polymorphic |
|
BOOST_STATIC_ASSERT(boost::is_polymorphic< T >::value); |
|
return static_cast<basic_pointer_iserializer *>(NULL); |
|
} |
|
}; |
|
|
|
struct non_abstract |
|
{ |
|
template<class T> |
|
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, const T & /*t*/){ |
|
// 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< |
|
boost::serialization::is_abstract<const T>, |
|
boost::mpl::identity<abstract>, |
|
boost::mpl::identity<non_abstract> |
|
>::type typex; |
|
return typex::template register_type< T >(ar); |
|
} |
|
|
|
template<class T> |
|
static T * pointer_tweak( |
|
const boost::serialization::extended_type_info & eti, |
|
void const * const t, |
|
const T & |
|
) { |
|
// tweak the pointer back to the base class |
|
return static_cast<T *>( |
|
const_cast<void *>( |
|
boost::serialization::void_upcast( |
|
eti, |
|
boost::serialization::singleton< |
|
BOOST_DEDUCED_TYPENAME |
|
boost::serialization::type_info_implementation< T >::type |
|
>::get_const_instance(), |
|
t |
|
) |
|
) |
|
); |
|
} |
|
|
|
template<class T> |
|
static void check_load(T & /* t */){ |
|
check_pointer_level< T >(); |
|
check_pointer_tracking< T >(); |
|
} |
|
|
|
static const basic_pointer_iserializer * |
|
find(const boost::serialization::extended_type_info & type){ |
|
return static_cast<const basic_pointer_iserializer *>( |
|
archive_serializer_map<Archive>::find(type) |
|
); |
|
} |
|
|
|
template<class Tptr> |
|
static void invoke(Archive & ar, Tptr & t){ |
|
check_load(*t); |
|
const basic_pointer_iserializer * bpis_ptr = register_type(ar, *t); |
|
const basic_pointer_iserializer * newbpis_ptr = ar.load_pointer( |
|
// note major hack here !!! |
|
// I tried every way to convert Tptr &t (where Tptr might |
|
// include const) to void * &. This is the only way |
|
// I could make it work. RR |
|
(void * & )t, |
|
bpis_ptr, |
|
find |
|
); |
|
// if the pointer isn't that of the base class |
|
if(newbpis_ptr != bpis_ptr){ |
|
t = pointer_tweak(newbpis_ptr->get_eti(), t, *t); |
|
} |
|
} |
|
}; |
|
|
|
template<class Archive> |
|
struct load_enum_type { |
|
template<class T> |
|
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> |
|
struct load_array_type { |
|
template<class T> |
|
static void invoke(Archive &ar, T &t){ |
|
typedef BOOST_DEDUCED_TYPENAME remove_extent< T >::type value_type; |
|
|
|
// convert integers to correct enum to load |
|
// determine number of elements in the array. Consider the |
|
// fact that some machines will align elements on boundries |
|
// other than characters. |
|
std::size_t current_count = sizeof(t) / ( |
|
static_cast<char *>(static_cast<void *>(&t[1])) |
|
- static_cast<char *>(static_cast<void *>(&t[0])) |
|
); |
|
boost::serialization::collection_size_type count; |
|
ar >> BOOST_SERIALIZATION_NVP(count); |
|
if(static_cast<std::size_t>(count) > current_count) |
|
boost::serialization::throw_exception( |
|
archive::archive_exception( |
|
boost::archive::archive_exception::array_size_too_short |
|
) |
|
); |
|
ar >> serialization::make_array(static_cast<value_type*>(&t[0]),count); |
|
} |
|
}; |
|
|
|
} // detail |
|
|
|
template<class Archive, class T> |
|
inline void load(Archive & ar, T &t){ |
|
// if this assertion trips. It means we're trying to load a |
|
// const object with a compiler that doesn't have correct |
|
// funtion template ordering. On other compilers, this is |
|
// handled below. |
|
detail::check_const_loading< T >(); |
|
typedef |
|
BOOST_DEDUCED_TYPENAME mpl::eval_if<is_pointer< T >, |
|
mpl::identity<detail::load_pointer_type<Archive> > |
|
,//else |
|
BOOST_DEDUCED_TYPENAME mpl::eval_if<is_array< T >, |
|
mpl::identity<detail::load_array_type<Archive> > |
|
,//else |
|
BOOST_DEDUCED_TYPENAME mpl::eval_if<is_enum< T >, |
|
mpl::identity<detail::load_enum_type<Archive> > |
|
,//else |
|
mpl::identity<detail::load_non_pointer_type<Archive> > |
|
> |
|
> |
|
>::type typex; |
|
typex::invoke(ar, t); |
|
} |
|
|
|
#if 0 |
|
|
|
// BORLAND |
|
#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x560)) |
|
// borland has a couple of problems |
|
// a) if function is partially specialized - see below |
|
// const paramters are transformed to non-const ones |
|
// b) implementation of base_object can't be made to work |
|
// correctly which results in all base_object s being const. |
|
// So, strip off the const for borland. This breaks the trap |
|
// for loading const objects - but I see no alternative |
|
template<class Archive, class T> |
|
inline void load(Archive &ar, const T & t){ |
|
load(ar, const_cast<T &>(t)); |
|
} |
|
#endif |
|
|
|
// let wrappers through. |
|
#ifndef BOOST_NO_FUNCTION_TEMPLATE_ORDERING |
|
template<class Archive, class T> |
|
inline void load_wrapper(Archive &ar, const T&t, mpl::true_){ |
|
boost::archive::load(ar, const_cast<T&>(t)); |
|
} |
|
|
|
#if !BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x560)) |
|
template<class Archive, class T> |
|
inline void load(Archive &ar, const T&t){ |
|
load_wrapper(ar,t,serialization::is_wrapper< T >()); |
|
} |
|
#endif |
|
#endif |
|
|
|
#endif |
|
|
|
} // namespace archive |
|
} // namespace boost |
|
|
|
#endif // BOOST_ARCHIVE_DETAIL_ISERIALIZER_HPP
|
|
|