// -*- tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
// vi: set et ts=8 sw=4 sts=4:
// $Id: smartpointer.hh 5504 2009-04-08 13:35:31Z christi $

#ifndef DUNE_SHARED_PTR_HH
#define DUNE_SHARED_PTR_HH

#if defined SHARED_PTR_HEADER
# include SHARED_PTR_HEADER
#endif
#if defined HAVE_BOOST_SHARED_PTR_HPP
#if defined HAVE_BOOST_MAKE_SHARED_HPP
# include <boost/make_shared.hpp>
#endif
#endif

#include<dune/common/nullptr.hh>
#include<dune/common/typetraits.hh>
/**
 * @file
 * @brief This file implements the class shared_ptr (a reference counting
 * pointer), for those systems that don't have it in the standard library.
 * @author Markus Blatt
 */
namespace Dune
{
    // A shared_ptr implementation has been found if SHARED_PTR_NAMESPACE is set at all
#ifdef SHARED_PTR_NAMESPACE
    using SHARED_PTR_NAMESPACE :: shared_ptr;
#else

    /** @addtogroup Common
     *
     * @{
     */

    /** @brief The object we reference. */  
    class SharedCount
    {
        template<class T1>
        friend class shared_ptr;
    protected:
        /** @brief The number of references. */
        int count_;
        /** @brief Constructor from existing Pointer. */
        SharedCount() : count_(1) {}
        /** @brief Copy constructor with type conversion. */
        SharedCount(const SharedCount& rep) 
            : count_(rep.count_) {}
        
        /** @brief Destructor, deletes element_type* rep_. */
        virtual ~SharedCount() {};
        
    };
    /**
     * @brief A reference counting smart pointer.
     *
     * It is designed such that it is usable within a std::vector.
     * The contained object is destroyed only if there are no more
     * references to it.
     */
    template<class T>
    class shared_ptr
    {
        template<class T1> friend class shared_ptr;

    public:
        /**
         * @brief The data type we are a pointer for.
         *
         * This has to have a parameterless constructor.
         */
        typedef T element_type;

        /** 
         * @brief Constructs a new smart pointer and allocates the referenced Object.
         */
        inline shared_ptr();
        
        inline shared_ptr(nullptr_t null);
        
        /** 
         * @brief Constructs a new smart pointer from a preallocated Object.
         *
         * \param pointer Raw pointer to the shared data
         *
         * note: the object must be allocated on the heap and after handing the pointer to
         * shared_ptr the ownership of the pointer is also handed to the shared_ptr.
         */
        template<class T1>
        inline shared_ptr(T1 * pointer);

        
        /** 
         * @brief Constructs a new smart pointer from a preallocated Object.
         *
         * \tparam Deleter This class must by copyconstructable, the copy constructor must not throw an exception
         * and it must implement void operator() (T*) const
         *
         * \param pointer Raw pointer to the shared data
         * \param deleter A copy of this deleter is stored
         *
         * note: the object must be allocated on the heap and after handing the pointer to
         * shared_ptr the ownership of the pointer is also handed to the shared_ptr.
         */
        template<class T1, class Deleter>
        inline shared_ptr(T1 * pointer, Deleter deleter);
        
        /**
         * @brief Copy constructor.
         * @param pointer The object to copy.
         */
        template<class T1>
        inline shared_ptr(const shared_ptr<T1>& pointer);

        /**
         * @brief Copy constructor.
         * @param pointer The object to copy.
         */
        inline shared_ptr(const shared_ptr& pointer);

        /**
         * @brief Destructor.
         */
        inline ~shared_ptr();

        /** \brief Assignment operator */
        template<class T1>
        inline shared_ptr& operator=(const shared_ptr<T1>& pointer);

        /** \brief Assignment operator */
        inline shared_ptr& operator=(const shared_ptr& pointer);

        /** \brief Dereference as object */
        inline element_type& operator*();
        
        /** \brief Dereference as pointer */
        inline element_type* operator->();
      
        /** \brief Dereference as const object */
        inline const element_type& operator*() const;
        
        /** \brief Dereference as const pointer */
        inline const element_type* operator->() const;

        /** \brief Access to the raw pointer, if you really want it */
        element_type* get() const {
            return rep_;
        }

        /** \brief Checks if shared_ptr manages an object, i.e. whether get() != 0. */ 
        operator bool() const {
            return count_ != 0 && rep_ != 0;
        }
        
        /** \brief Swap content of this shared_ptr and another */
        inline void swap(shared_ptr& other);

        /** \brief Decrease the reference count by one and free the memory if the
            reference count has reached 0
        */
        inline void reset();

        /** \brief Detach shared pointer and set it anew for the given pointer */
        template<class T1>
        inline void reset(T1* pointer);

        //** \brief Same as shared_ptr(pointer,deleter).swap(*this)
        template<class T1, class Deleter>
        inline void reset(T1* pointer, Deleter deleter);

        /** \brief The number of shared_ptrs pointing to the object we point to */
        int use_count() const;

    private:
        /** \brief Assignment operator */
        template<class T1>
        inline shared_ptr& assign(const shared_ptr<T1>& pointer);
        /** @brief Adds call to deleter to SharedCount. */  
        template<class Deleter>
        class SharedCountImpl :
            public SharedCount
        {
            template<class T1>
            friend class shared_ptr;
            /** @brief Constructor from existing Pointer with custom deleter. */
            SharedCountImpl(T* elem,const Deleter& deleter) :
                SharedCount(),
                rep_(elem),
                deleter_(deleter)
            {}
            /** @brief Copy constructor with type conversion. */
            SharedCountImpl(const SharedCountImpl& rep) 
                : SharedCount(rep), deleter_(rep.deleter_) {}
            /** @brief Destructor, deletes element_type* rep_ using deleter. */
            ~SharedCountImpl()
            { deleter_(rep_); }
            
            // store a copy of the deleter
            Deleter deleter_;
            T* rep_;
        };

        /** \brief A default deleter that just calls delete */
        struct DefaultDeleter
        {
            void operator() (element_type* p) const
            { delete p; }
        };


        SharedCount *count_;
        T *rep_;

        // Needed for the implicit conversion to "bool"
        typedef T* *__unspecified_bool_type;
        
    public:
        /** \brief Implicit conversion to "bool" */
        operator __unspecified_bool_type() const // never throws
        { 
            return rep_ == 0 ? 0 : &shared_ptr::rep_; 
        }

    };

    template<class T>
    template<class T1>
    inline shared_ptr<T>::shared_ptr(T1 * p)
    {
        rep_ = p;
        count_ = new SharedCountImpl<DefaultDeleter>(p, DefaultDeleter());
    }

    template<class T>
    inline shared_ptr<T>::shared_ptr(nullptr_t n)
    {
        rep_   = 0;
        count_ = 0;
    }
    
    template<class T>
    template<class T1, class Deleter>
    inline shared_ptr<T>::shared_ptr(T1 * p, Deleter deleter)
    {
        rep_ = p;
        count_ = new SharedCountImpl<Deleter>(p, deleter);
    }

    template<class T>
    inline shared_ptr<T>::shared_ptr()
    {
        rep_ = 0;
        count_=0;
    }

    template<class T>
    template<class T1>
    inline shared_ptr<T>::shared_ptr(const shared_ptr<T1>& other) 
        : rep_(other.rep_), count_(other.count_)
    {
        if (rep_)
            ++(count_->count_);
    }
    
    template<class T>
    inline shared_ptr<T>::shared_ptr(const shared_ptr& other)
        : rep_(other.rep_), count_(other.count_)
    {
        if (rep_)
            ++(count_->count_);
    }

    template<class T>
    template<class T1>
    inline shared_ptr<T>& shared_ptr<T>::operator=(const shared_ptr<T1>& other)
    {
        return assign(other);
    }

    template<class T>
    inline shared_ptr<T>& shared_ptr<T>::operator=(const shared_ptr& other)
    {
        return assign(other);
    }

    template<class T>
    template<class T1>
    inline shared_ptr<T>& shared_ptr<T>::assign(const shared_ptr<T1>& other)
    {
        if (other.count_)
            (other.count_->count_)++;
        
        if(rep_!=0 && --(count_->count_)<=0){
            delete count_;
        }
        
        rep_ = other.rep_;
        count_ = other.count_;
        return *this;
    }

    template<class T>
    inline shared_ptr<T>::~shared_ptr()
    {
        if(rep_!=0 && --(count_->count_)==0){
            delete count_;
            rep_=0;
        }
    }

    template<class T>
    inline T& shared_ptr<T>::operator*()
    {
        return *(rep_);
    }

    template<class T>
    inline T *shared_ptr<T>::operator->()
    {
        return rep_;
    }

    template<class T>
    inline const T& shared_ptr<T>::operator*() const
    {
        return *(rep_);
    }

    template<class T>
    inline const T *shared_ptr<T>::operator->() const
    {
        return rep_;
    }
    
    template<class T>
    inline int shared_ptr<T>::use_count() const
    {
        return count_->count_;
    }

    template<class T>
    inline void shared_ptr<T>::swap(shared_ptr<T>& other)
    {
        SharedCount* dummy = count_;
        count_=other.count_;
        other.count_ = dummy;
        T* tdummy=rep_;
        rep_ = other.rep_;
        other.rep_ = tdummy;
    }

    template<class T>
    inline void shared_ptr<T>::reset()
    {
        shared_ptr<T>().swap(*this);
    }

    template<class T>
    template<class T1>
    inline void shared_ptr<T>::reset(T1* pointer)
    {
        shared_ptr<T>(pointer).swap(*this);
    }

    template<class T>
    template<class T1, class Deleter>
    inline void shared_ptr<T>::reset(T1* pointer, Deleter deleter)
    {
        shared_ptr<T>(pointer, deleter).swap(*this);
    }

    /** @} */
#endif  // #ifdef SHARED_PTR_NAMESPACE


// C++0x and Boost have a make_shared implementation, TR1 does not.
// Unfortunately, TR1 gets picked over Boost if present.
// Moreover, boost::make_shared() only exists for (remotely) recent versions of Boost.
#if HAVE_MAKE_SHARED
#ifdef SHARED_PTR_NAMESPACE
    using SHARED_PTR_NAMESPACE :: make_shared;
#endif
#else

    template<typename T>
    shared_ptr<T> make_shared()
    {
      return shared_ptr<T>(new T());
    }

    template<typename T, typename Arg1>
    shared_ptr<T> make_shared(const Arg1& arg1)
    {
      return shared_ptr<T>(new T(arg1));
    }

    template<typename T, typename Arg1, typename Arg2>
    shared_ptr<T> make_shared(const Arg1& arg1, const Arg2& arg2)
    {
      return shared_ptr<T>(new T(arg1,arg2));
    }

    template<typename T, typename Arg1, typename Arg2, typename Arg3>
    shared_ptr<T> make_shared(const Arg1& arg1, const Arg2& arg2, const Arg3& arg3)
    {
      return shared_ptr<T>(new T(arg1,arg2,arg3));
    }

    template<typename T, typename Arg1, typename Arg2, typename Arg3, typename Arg4>
    shared_ptr<T> make_shared(const Arg1& arg1, const Arg2& arg2, const Arg3& arg3, const Arg4& arg4)
    {
      return shared_ptr<T>(new T(arg1,arg2,arg3,arg4));
    }

    template<typename T, typename Arg1, typename Arg2, typename Arg3, typename Arg4,
             typename Arg5>
    shared_ptr<T> make_shared(const Arg1& arg1, const Arg2& arg2, const Arg3& arg3, const Arg4& arg4,
                              const Arg5& arg5)
    {
      return shared_ptr<T>(new T(arg1,arg2,arg3,arg4,arg5));
    }

    template<typename T, typename Arg1, typename Arg2, typename Arg3, typename Arg4,
             typename Arg5, typename Arg6>
    shared_ptr<T> make_shared(const Arg1& arg1, const Arg2& arg2, const Arg3& arg3, const Arg4& arg4,
                              const Arg5& arg5, const Arg6& arg6)
    {
      return shared_ptr<T>(new T(arg1,arg2,arg3,arg4,arg5,arg6));
    }

    template<typename T, typename Arg1, typename Arg2, typename Arg3, typename Arg4,
             typename Arg5, typename Arg6, typename Arg7>
    shared_ptr<T> make_shared(const Arg1& arg1, const Arg2& arg2, const Arg3& arg3, const Arg4& arg4,
                              const Arg5& arg5, const Arg6& arg6, const Arg7& arg7)
    {
      return shared_ptr<T>(new T(arg1,arg2,arg3,arg4,arg5,arg6,arg7));
    }

    template<typename T, typename Arg1, typename Arg2, typename Arg3, typename Arg4,
             typename Arg5, typename Arg6, typename Arg7, typename Arg8>
    shared_ptr<T> make_shared(const Arg1& arg1, const Arg2& arg2, const Arg3& arg3, const Arg4& arg4,
                              const Arg5& arg5, const Arg6& arg6, const Arg7& arg7, const Arg8& arg8)
    {
      return shared_ptr<T>(new T(arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8));
    }

    template<typename T, typename Arg1, typename Arg2, typename Arg3, typename Arg4,
             typename Arg5, typename Arg6, typename Arg7, typename Arg8, typename Arg9>
    shared_ptr<T> make_shared(const Arg1& arg1, const Arg2& arg2, const Arg3& arg3, const Arg4& arg4,
                              const Arg5& arg5, const Arg6& arg6, const Arg7& arg7, const Arg8& arg8,
                              const Arg9& arg9)
    {
      return shared_ptr<T>(new T(arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9));
    }

#endif // custom make_shared

    /**
       @brief implements the Deleter concept of shared_ptr without deleting anything
       @relates shared_ptr

       If you allocate an object on the stack, but want to pass it to a class or function as a shared_ptr,
       you can use this deleter to avoid accidental deletion of the stack-allocated object.

       For convenience we provide two free functions to create a shared_ptr from a stack-allocated object
       (\see stackobject_to_shared_ptr):

       1) Convert a stack-allocated object to a shared_ptr:
       @code
            int i = 10;
            shared_ptr<int> pi = stackobject_to_shared_ptr(i);
       @endcode
       2) Convert a stack-allocated object to a shared_ptr of a base class
       @code
            class A {};
            class B : public A {};

            ...
            
            B b;
            shared_ptr<A> pa = stackobject_to_shared_ptr<A>(b);
       @endcode
       
       @tparam T type of the stack-allocated object
     */
    template<class T>
    struct null_deleter
    {
        void operator() (T* p) const {}
    };

    /**
       @brief Convert a stack-allocated object to a shared_ptr:
       @relates shared_ptr
       @code
            int i = 10;
            shared_ptr<int> pi = stackobject_to_shared_ptr(i);
       @endcode
     */
    template<typename T>
    inline shared_ptr<T> stackobject_to_shared_ptr(T & t)
    {
        return shared_ptr<T>(&t, null_deleter<T>());
    }

    /**
       @brief Convert a stack object to a shared_ptr of a base class
       @relates shared_ptr
       @code
            class A {};
            class B : public A {};

            ...
            
            B b;
            shared_ptr<A> pa = stackobject_to_shared_ptr<A>(b);
       @endcode
     */
    template<typename T, typename T2>
    inline shared_ptr<T2> stackobject_to_shared_ptr(T & t)
    {
        return shared_ptr<T2>(dynamic_cast<T2*>(&t), null_deleter<T2>());
    }

}
#endif
