/*****************************************************************************
 * $LastChangedDate: 2010-05-25 21:26:05 -0400 (Tue, 25 May 2010) $
 * @file
 * @author  Jim E. Brooks  http://www.palomino3d.org
 * @brief   Template class to shield a pointer from deletion.
 *//*
 * LEGAL:   COPYRIGHT (C) 2006 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE VERSION 2 (GPL 2).
 *****************************************************************************/

#ifndef BASE_SAFE_PTR_HH
#define BASE_SAFE_PTR_HH 1

namespace base {

////////////////////////////////////////////////////////////////////////////////
// (Documented at safe_ptr_doc.hh)
//
template<typename T>
class SafePtr
{
public:
  //SafePtr( void ) : mObj(NULL) { }  // perpetrator of false advertisement
    SafePtr( T* obj ) : mObj(obj) { }
    ~SafePtr() { }  // SafePtr<> doesn't own actual object

    // Copy ctors for related types.
    // SafePtr<Base> = new Derived (v.v. is illegal and won't compile).
    template<typename T2> SafePtr( T2* obj ) : mObj(obj) { }
    template<typename T2> SafePtr( const SafePtr<T2> ptr ) : mObj(ptr.mObj) { }

    T*      operator->( void ) const { return mObj; }
    T&      operator*( void ) const { return *mObj; }  // dereference, not multiply

// Methods for identical types:

    // Assign this SafePtr with an actual pointer.
    SafePtr<T>& operator=( T* obj ) { mObj = obj; return *this; }

    // Compare addresses of actual objects of same type.
    bool    operator==( const SafePtr& other ) const { return mObj == other.mObj; }
    bool    operator!=( const SafePtr& other ) const { return mObj != other.mObj; }

// Methods for related types (base and derived):

    // Assign SafePtr<> with an actual pointer to a related type.
    // SafePtr<Base> ptrBase = new Derived;  // ok
    // SafePtr<Derived> ptrDerived = new Base;  // wrong (missing slice), should not compile
    template<typename T2> SafePtr<T>& operator=( T2* obj )
    {
        mObj = obj;    // Base* obj = pDerived (T=Base T2=Derived)
        return *this;  // return SafePtr<Base>
    }

    // Assign SafePtr<Base> = SafePtr<Derived> (v.v. is illegal).
    // Equivalent to: Base* pBase = pDerived;
    template<typename T2> SafePtr<T>& operator=( SafePtr<T2> ptr )
    {
        T* obj = ptr.mObj;  // Base* base = SafePtr<Derived>.mObj (T=Base T2=Derived)
        mObj = obj;         // Base* mObj = base
        return *this;       // return SafePtr<Base>
    }

    // Limited use.
    T*             PTR( void ) const { return mObj; }
    T*             get( void ) const { return mObj; }
    const T* CONST_PTR( void ) const { return mObj; }
    T&             REF( void ) const { return *mObj; }
    const T& CONST_REF( void ) const { return *mObj; }

private:
    T*      mObj;

template<typename> friend class SafePtr;  // variations of SafePtr<> are distinct types so friend is necessary
};

// Comparing addresses of actual objects.
template<typename T>              bool operator==( const SafePtr<T>& ptr, const T* obj )          { return ptr.CONST_PTR() == obj; }
template<typename T>              bool operator!=( const SafePtr<T>& ptr, const T* obj )          { return ptr.CONST_PTR() != obj; }
template<typename T>              bool operator==( const T* obj, const SafePtr<T>& ptr )          { return ptr.CONST_PTR() == obj; }
template<typename T>              bool operator!=( const T* obj, const SafePtr<T>& ptr )          { return ptr.CONST_PTR() != obj; }
template<typename T1,typename T2> bool operator==( const T1* obj, const SafePtr<T2>& ptr )        { return ptr.CONST_PTR() == obj; }
template<typename T1,typename T2> bool operator!=( const T1* obj, const SafePtr<T2>& ptr )        { return ptr.CONST_PTR() != obj; }
template<typename T1,typename T2> bool operator==( const SafePtr<T1>& ptr1, const SafePtr<T2>& ptr2 ) { return ptr1.CONST_PTR() == ptr2.CONST_PTR(); }
template<typename T1,typename T2> bool operator!=( const SafePtr<T1>& ptr1, const SafePtr<T2>& ptr2 ) { return ptr1.CONST_PTR() != ptr2.CONST_PTR(); }
template<typename T>              bool operator<(  const SafePtr<T>& ptr1, const SafePtr<T>& ptr2 )   { return ptr1.CONST_PTR()  < ptr2.CONST_PTR(); }

} // namespace base

#endif // BASE_SAFE_PTR_HH
