/*****************************************************************************
 * $Id: gfx_safe_ptr.hh,v 1.3 2006/11/02 02:27:40 devel Exp $
 * @file
 * @author	Jim E. Brooks  http://www.jimbrooks.org
 * @brief	Template class to shield a pointer from deletion.
 *//*
 * LEGAL:	COPYRIGHT (C) JIM E. BROOKS 2006
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE (GPL).
 *****************************************************************************/

////////////////////////////////////////////////////////////////////////////////
/// @class gfx::SafePtr
/// @brief Template class to shield an object being deleting by its users.
///
/// @verbatim
///
/// Template class to shield an object being deleting by its pointer
/// and to help the object's creator to retain ownership.
/// It prevents the pitfall of passing pointers around
/// and some subroutine deletes it by mistake.
///
/// Remember:
/// ---------
/// - Return "const Ptr<>" instead of just "Ptr<>" (covers pitfall).
/// - Passing "const Ptr<>" is recommended.
/// - Pass Ptr<> by value (by reference is actually slower)
/// - Be careful using Ptr<> in existing code that casts pointers.
///
/// These typedefs can optimize non-debug builds.
/// "ObjectPtrConst" means a "const Ptr<>" and corresponds to "Object* const" (constant pointer).
/// #if DEBUG
///     typedef       Ptr<Object> ObjectPtr;
///     typedef const Ptr<Object> ObjectPtrConst;
/// #else
///     typedef Object*       ObjectPtr;
///     typedef Object* const ObjectPtrConst;
/// #endif
///
/// WARNING:
/// --------
/// SafePtr<> suffers from the assignment-to-temporary pitfall.
/// The cure is const: functions should return "const SafePtr<>", not just "SafePtr<>".
/// For example, changing mData to private and interfacing it with
/// a getter method opens the pitfall:
///     SafePtr<Object> GetObject(void);
///     mObject = obj;      // old code
///     GetObject() = obj;  // new code: oops! A temporary Ptr<> object was assigned!
///
/// Design notes:
/// -------------
/// Omitted is a conversion operator from SafePtr<T> to T* as that
/// would defeat the purpose by making "delete ptr" compile.
/// operator bool() is implicitly defined for actual pointers but omitted for SafePtr<>,
/// so writing explicitly writing NULL is required: "if (ptr != NULL)"
/// and the requirement helps finding/eliminating NULL states.
///
/// @endverbatim
////////////////////////////////////////////////////////////////////////////////

#ifndef GFX_SAFE_PTR_HH
#define GFX_SAFE_PTR_HH

namespace gfx {

template<typename T>
class SafePtr
{
public:
            SafePtr( void ) : mObj(NULL) { } 
            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>
    }

#if ! GCC29
private:
#endif
    T*		mObj;

template<typename OBJ> friend       OBJ* GET_PTR( const SafePtr<OBJ> ptr );  // not "T"
template<typename OBJ> friend const OBJ* GET_CONST_PTR( const SafePtr<OBJ> ptr );

#if ! GCC29  // friend declaration causes ICE on gcc 2.95.3
template<typename> friend class SafePtr;  // variations of SafePtr<> are distinct types so friend is necessary
#endif
};

// Restricted usage.
template<typename T>       T*       GET_PTR( const SafePtr<T> ptr ) { return ptr.mObj; }
template<typename T> const T* GET_CONST_PTR( const SafePtr<T> ptr ) { return ptr.mObj; }
// For compatibility with compiling SafePtr<> or actual pointers in debug or release builds, resp.
// gcc will optimize these as efficiently as macros.
template<typename T>       T*       GET_PTR( T* obj ) { return obj; }
template<typename T> const T* GET_CONST_PTR( T* obj ) { return obj; }

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

} // namespace gfx

#endif // GFX_SAFE_PTR_HH
