// Copyright (C) 2007  Davis E. King (davis@dlib.net)
// License: Boost Software License   See LICENSE.txt for the full license.
#undef DLIB_SHARED_PTr_ABSTRACT_
#ifdef DLIB_SHARED_PTr_ABSTRACT_ 

#include "weak_ptr_abstract.h"
#include <exception>     

namespace dlib 
{

// ----------------------------------------------------------------------------------------

    class bad_weak_ptr: public std::exception {}

// ----------------------------------------------------------------------------------------

    template <
        typename T
        > 
    class shared_ptr 
    {
        /*!
            INITIAL VALUE
                defined by constructors

            WHAT THIS OBJECT REPRESENTS
                This object represents a reference counted smart pointer.  Each shared_ptr
                contains a pointer to some object and when the last shared_ptr that points
                to the object is destructed or reset() then the object is guaranteed to be 
                deleted.

                This is an implementation of the std::tr1::shared_ptr template from the 
                document ISO/IEC PDTR 19768, Proposed Draft Technical Report on C++
                Library Extensions.  The only deviation from that document is that this 
                shared_ptr is declared inside the dlib namespace rather than std::tr1.

            THREAD SAFETY
                This object is not thread safe.  Especially so since it is
                reference counted.  So you should take care to not have two shared_ptr
                objects in different threads that point to the same object.

                If you want a thread safe version of this object you should use the
                dlib::shared_ptr_thread_safe object instead.
        !*/

    public:

        typedef T element_type;

        shared_ptr(
        );
        /*!
            ensures
                - #get() == 0
                - #use_count() == 0
        !*/

        template<typename Y> 
        explicit shared_ptr(
            Y* p
        );
        /*!
            requires
                - p is convertible to a T* type pointer
                - p can be deleted by calling "delete p;" and doing so will not throw exceptions
                - p != 0
            ensures
                - #get() == p
                - #use_count() == 1
                - #*this object owns the pointer p
            throws
                - std::bad_alloc
                  if this exception is thrown then "delete p;" is called
        !*/

        template<typename Y, typename D> 
        shared_ptr(
            Y* p, 
            const D& d
        );
        /*!
            requires
                - p is convertible to a T* type pointer
                - D is copy constructable (and the copy constructor of D doesn't throw) 
                - p can be deleted by calling "d(p);" and doing so will not throw exceptions
                - p != 0
            ensures
                - #get() == p
                - #use_count() == 1
                - #*this object owns the pointer p
            throws
                - std::bad_alloc
                  if this exception is thrown then "d(p);" is called
        !*/

        shared_ptr( 
            const shared_ptr& r
        );
        /*!
            ensures
                - #get() == #r.get()
                - #use_count() == #r.use_count()
                - If r is empty, constructs an empty shared_ptr object; otherwise, constructs 
                  a shared_ptr object that shares ownership with r.
        !*/

        template<typename Y> 
        shared_ptr(
            const shared_ptr<Y>& r
        );
        /*!
            requires
                - Y* is convertible to T* 
            ensures
                - #get() == #r.get()
                - #use_count() == #r.use_count()
                - If r is empty, constructs an empty shared_ptr object; otherwise, constructs 
                  a shared_ptr object that shares ownership with r.
        !*/

        template<typename Y> 
        explicit shared_ptr(
            const weak_ptr<Y>& r
        );
        /*!
            requires
                - Y* is convertible to T* 
            ensures
                - #get() == #r.get()
                - #use_count() == #r.use_count()
                - If r is empty, constructs an empty shared_ptr object; otherwise, constructs 
                  a shared_ptr object that shares ownership with r.
            throws
                - bad_weak_ptr
                  this exception is thrown if r.expired() == true
        !*/

        template<typename Y>
        explicit shared_ptr(
            std::auto_ptr<Y>& r
        );
        /*!
            requires
                - p.get() != 0
                - p.release() is convertible to a T* type pointer
                - p.release() can be deleted by calling "delete p.release();" and doing so will not throw exceptions
            ensures
                - #get() == p.release()
                - #use_count() == 1
                - #r.get() == 0
                - #*this object owns the pointer p.release()
            throws
                - std::bad_alloc
        !*/

        ~shared_ptr(
        );
        /*!
            ensures
                - if (use_count() > 1)
                    - this object destroys itself but otherwise has no effect (i.e. 
                      the pointer get() is still valid and shared between the remaining
                      shared_ptr objects)
                - else if (use_count() == 1)
                    - deletes the pointer get() by calling delete (or using the deleter passed
                      to the constructor if one was passed in)
                - else
                    - in this case get() == 0 so there is nothing to do so nothing occurs
        !*/

        shared_ptr& operator= (
            const shared_ptr& r
        );
        /*!
            ensures
                - equivalent to shared_ptr(r).swap(*this).
                - returns #*this
        !*/

        template<typename Y> 
        shared_ptr& operator= (
            const shared_ptr<Y>& r
        );
        /*!
            requires
                - Y* is convertible to T* 
            ensures
                - equivalent to shared_ptr(r).swap(*this).
                - returns #*this
        !*/

        template<typename Y> 
        shared_ptr& operator= (
            std::auto_ptr<Y>& r
        );
        /*!
            requires
                - p.get() != 0
                - p.release() is convertible to a T* type pointer
                - p.release() can be deleted by calling "delete p.release();" and doing so will not throw exceptions
            ensures
                - equivalent to shared_ptr(r).swap(*this).
                - returns #*this
        !*/

        void reset(
        );
        /*!
            ensures
                - equivalent to shared_ptr().swap(*this)
        !*/

        template<typename Y> 
        void reset(
            Y* p
        );
        /*!
            requires
                - p is convertible to a T* type pointer
                - p can be deleted by calling "delete p;" and doing so will not throw exceptions
                - p != 0
            ensures
                - equivalent to shared_ptr(p).swap(*this)
        !*/

        template<typename Y, typename D> 
        void reset(
            Y* p, 
            const D& d
        );
        /*!
            requires
                - p is convertible to a T* type pointer
                - D is copy constructable (and the copy constructor of D doesn't throw) 
                - p can be deleted by calling "d(p);" and doing so will not throw exceptions
                - p != 0
            ensures
                - equivalent to shared_ptr(p,d).swap(*this)
        !*/

        T* get(
        ) const; 
        /*!
            ensures
                - returns the stored pointer
        !*/

        T& operator*(
        ) const; 
        /*!
            requires
                - get() != 0
            ensures
                - returns a reference to *get()
        !*/

        T* operator->(
        ) const; 
        /*!
            requires
                - get() != 0
            ensures
                - returns get()
        !*/

        bool unique(
        ) const;
        /*!
            ensures
                - returns (use_count() == 1)
        !*/

        long use_count(
        ) const;
        /*!
            ensures
                - The number of shared_ptr objects, *this included, that share ownership with *this, or 0 when *this
                  is empty.
        !*/

        operator bool(
        ) const;
        /*!
            ensures
                - returns (get() != 0)
        !*/

        void swap(
            shared_ptr& b
        );
        /*!
            ensures
                - swaps *this and item
        !*/

    };

// ----------------------------------------------------------------------------------------

    template<typename T, typename U>
    bool operator== (
        const shared_ptr<T>& a, 
        const shared_ptr<U>& b
    );
    /*!
        ensures
            - returns a.get() == b.get()
    !*/

    template<typename T, typename U>
    bool operator!= (
        const shared_ptr<T>& a, 
        const shared_ptr<U>& b
    ) { return a.get() != b.get(); }
    /*!
        ensures
            - returns a.get() != b.get()
    !*/

    template<typename T, typename U>
    bool operator< (
        const shared_ptr<T>& a, 
        const shared_ptr<U>& b
    );
    /*!
        ensures
            - Defines an operator< on shared_ptr types appropriate for use in the associative 
              containers.  
    !*/

    template<typename T> 
    void swap(
        shared_ptr<T>& a, 
        shared_ptr<T>& b
    ) { a.swap(b); }
    /*!
        provides a global swap function
    !*/

    template<typename T, typename U>
    shared_ptr<T> static_pointer_cast(
        const shared_ptr<U>& r
    );
    /*!
        - if (r.get() == 0) then
            - returns shared_ptr<T>()
        - else
            - returns a shared_ptr<T> object that stores static_cast<T*>(r.get()) and shares 
              ownership with r.
    !*/

    template<typename T, typename U>
    shared_ptr<T> const_pointer_cast(
        const shared_ptr<U>& r
    );
    /*!
        - if (r.get() == 0) then
            - returns shared_ptr<T>()
        - else
            - returns a shared_ptr<T> object that stores const_cast<T*>(r.get()) and shares 
              ownership with r.
    !*/

    template<typename T, typename U>
    shared_ptr<T> dynamic_pointer_cast(
        const shared_ptr<U>& r
    );
    /*!
        ensures
            - if (dynamic_cast<T*>(r.get()) returns a nonzero value) then
                - returns a shared_ptr<T> object that stores a copy of 
                  dynamic_cast<T*>(r.get()) and shares ownership with r
            - else
                - returns an empty shared_ptr<T> object.
    !*/

    template<typename E, typename T, typename Y>
    std::basic_ostream<E, T> & operator<< (
        std::basic_ostream<E, T> & os, 
        const shared_ptr<Y>& p
    );
    /*!
        ensures
            - performs os << p.get()
            - returns os 
    !*/

    template<typename D, typename T>
    D* get_deleter(
        const shared_ptr<T>& p
    );
    /*!
        ensures
            - if (*this owns a deleter d of type cv-unqualified D) then
                - returns &d
            - else
                - returns 0
    !*/

// ----------------------------------------------------------------------------------------

}

#endif // DLIB_SHARED_PTr_ABSTRACT_

