#ifndef  BOOST_SERIALIZATION_VOID_CAST_HPP
#define BOOST_SERIALIZATION_VOID_CAST_HPP

// MS compatible compilers support #pragma once
#if defined(_MSC_VER)
# pragma once
#endif

/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
// void_cast.hpp:   interface for run-time casting of void pointers.

// (C) Copyright 2002-2009 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)
// gennadiy.rozental@tfn.com

//  See http://www.boost.org for updates, documentation, and revision history.

#include <cstddef> // for ptrdiff_t
#include <boost/config.hpp>
#include <boost/noncopyable.hpp>

#include <boost/serialization/smart_cast.hpp>
#include <boost/serialization/singleton.hpp>
#include <boost/serialization/force_include.hpp>
#include <boost/serialization/type_info_implementation.hpp>
#include <boost/serialization/extended_type_info.hpp>
#include <boost/type_traits/is_virtual_base_of.hpp>
#include <boost/serialization/void_cast_fwd.hpp>

#include <boost/serialization/config.hpp>
#include <boost/config/abi_prefix.hpp> // must be the last header

#ifdef BOOST_MSVC
#  pragma warning(push)
#  pragma warning(disable : 4251 4231 4660 4275)
#endif

namespace boost { 
namespace serialization { 

class extended_type_info;

// Given a void *, assume that it really points to an instance of one type
// and alter it so that it would point to an instance of a related type.
// Return the altered pointer. If there exists no sequence of casts that
// can transform from_type to to_type, return a NULL.  

BOOST_SERIALIZATION_DECL(void const *)
void_upcast(
    extended_type_info const & derived,  
    extended_type_info const & base, 
    void const * const t
);

inline void *
void_upcast(
    extended_type_info const & derived,
    extended_type_info const & base,
    void * const t 
){
    return const_cast<void*>(void_upcast(
        derived, 
        base, 
        const_cast<void const *>(t)
    ));
}

BOOST_SERIALIZATION_DECL(void const *)
void_downcast(
    extended_type_info const & derived,  
    extended_type_info const & base, 
    void const * const t
);

inline void *
void_downcast(
    extended_type_info const & derived,
    extended_type_info const & base,
    void * const t 
){
    return const_cast<void*>(void_downcast(
        derived, 
        base, 
        const_cast<void const *>(t)
    ));
}

namespace void_cast_detail {

class BOOST_SERIALIZATION_DECL(BOOST_PP_EMPTY()) void_caster :
    private boost::noncopyable
{
    friend 
    BOOST_SERIALIZATION_DECL(void const *)
    boost::serialization::void_upcast(
        extended_type_info const & derived,
        extended_type_info const & base,
        void const * const
    );
    friend 
    BOOST_SERIALIZATION_DECL(void const *)  
    boost::serialization::void_downcast(
        extended_type_info const & derived,
        extended_type_info const & base,
        void const * const
    );
protected:
    void recursive_register(bool includes_virtual_base = false) const;
    void recursive_unregister() const;
    virtual bool has_virtual_base() const = 0;
public:
    // Data members
    const extended_type_info * m_derived;
    const extended_type_info * m_base;
    /*const*/ std::ptrdiff_t m_difference;
    void_caster const * const m_parent;

    // note that void_casters are keyed on value of
    // member extended type info records - NOT their
    // addresses.  This is necessary in order for the
    // void cast operations to work across dll and exe
    // module boundries.
    bool operator<(const void_caster & rhs) const;

    const void_caster & operator*(){
        return *this;
    }
    // each derived class must re-implement these;
    virtual void const * upcast(void const * const t) const = 0;
    virtual void const * downcast(void const * const t) const = 0;
    // Constructor
    void_caster(
        extended_type_info const * derived,
        extended_type_info const * base,
        std::ptrdiff_t difference = 0,
        void_caster const * const parent = 0
    ) :
        m_derived(derived),
        m_base(base),
        m_difference(difference),
        m_parent(parent)
    {}
    virtual ~void_caster(){}
};

#ifdef BOOST_MSVC
#  pragma warning(push)
#  pragma warning(disable : 4251 4231 4660 4275 4511 4512)
#endif

template <class Derived, class Base>
class void_caster_primitive : 
    public void_caster
{
    virtual void const * downcast(void const * const t) const {
        const Derived * d = 
            boost::serialization::smart_cast<const Derived *, const Base *>(
                static_cast<const Base *>(t)
            );
        return d;
    }
    virtual void const * upcast(void const * const t) const {
        const Base * b = 
            boost::serialization::smart_cast<const Base *, const Derived *>(
                static_cast<const Derived *>(t)
            );
        return b;
    }
    virtual bool has_virtual_base() const {
        return false;
    }
public:
    void_caster_primitive();
    virtual ~void_caster_primitive();
};

template <class Derived, class Base>
void_caster_primitive<Derived, Base>::void_caster_primitive() :
    void_caster( 
        & type_info_implementation<Derived>::type::get_const_instance(), 
        & type_info_implementation<Base>::type::get_const_instance(),
        // note:I wanted to displace from 0 here, but at least one compiler
        // treated 0 by not shifting it at all.
        reinterpret_cast<std::ptrdiff_t>(
            static_cast<Derived *>(
                reinterpret_cast<Base *>(8)
            )
        ) - 8
    )
{
    recursive_register();
}

template <class Derived, class Base>
void_caster_primitive<Derived, Base>::~void_caster_primitive(){
    recursive_unregister();
}

template <class Derived, class Base>
class void_caster_virtual_base : 
    public void_caster
{
    virtual bool has_virtual_base() const {
        return true;
    }
public:
    virtual void const * downcast(void const * const t) const {
        const Derived * d = 
            dynamic_cast<const Derived *>(
                static_cast<const Base *>(t)
            );
        return d;
    }
    virtual void const * upcast(void const * const t) const {
        const Base * b = 
            dynamic_cast<const Base *>(
                static_cast<const Derived *>(t)
            );
        return b;
    }
    void_caster_virtual_base();
    virtual ~void_caster_virtual_base();
};

#ifdef BOOST_MSVC
#pragma warning(pop)
#endif

template <class Derived, class Base>
void_caster_virtual_base<Derived,Base>::void_caster_virtual_base() :
    void_caster( 
        & (type_info_implementation<Derived>::type::get_const_instance()), 
        & (type_info_implementation<Base>::type::get_const_instance())
    )
{
    recursive_register(true);
}

template <class Derived, class Base>
void_caster_virtual_base<Derived,Base>::~void_caster_virtual_base(){
    recursive_unregister();
}

template <class Derived, class Base>
struct void_caster_base :
    public void_caster
{
    typedef
        typename mpl::eval_if<boost::is_virtual_base_of<Base,Derived>,
            mpl::identity<
                void_cast_detail::void_caster_virtual_base<Derived, Base>
            >
        ,// else
            mpl::identity<
                void_cast_detail::void_caster_primitive<Derived, Base>
            >
        >::type type;
};

} // void_cast_detail 

template<class Derived, class Base>
BOOST_DLLEXPORT 
inline const void_cast_detail::void_caster & void_cast_register(
    Derived const * /* dnull = NULL */, 
    Base const * /* bnull = NULL */
){
    typedef
        typename mpl::eval_if<boost::is_virtual_base_of<Base,Derived>,
            mpl::identity<
                void_cast_detail::void_caster_virtual_base<Derived, Base>
            >
        ,// else
            mpl::identity<
                void_cast_detail::void_caster_primitive<Derived, Base>
            >
        >::type typex;
    return singleton<typex>::get_const_instance();
}

template<class Derived, class Base>
class void_caster :
    public void_cast_detail::void_caster_base<Derived, Base>::type
{
};

} // namespace serialization
} // namespace boost

#ifdef BOOST_MSVC  
#  pragma warning(pop)  
#endif

#include <boost/config/abi_suffix.hpp> // pops abi_suffix.hpp pragmas

#endif // BOOST_SERIALIZATION_VOID_CAST_HPP
