/*****************************************************************************
 * $Id: gfx_shared_ptr_2.hh,v 1.7 2006/10/12 01:43:24 devel Exp $
 * @file
 * @author  Jim E. Brooks  http://www.jimbrooks.org
 * @brief   Template class for sharing objects by pointer.
 *//*
 * LEGAL:   COPYRIGHT (C) JIM E. BROOKS 2005
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE (GPL).
 *****************************************************************************/

////////////////////////////////////////////////////////////////////////////////
/// @class gfx::SharedPtr
/// @brief Template class for objects shared by pointer using a reference-count.
///
/// @verbatim
///
/// Which to use?  SharedVal or SharedPtr?
/// --------------------------------------
/// See gfx_shared_val.hh.
///
/// Overview:
/// ---------
/// This is a template class for sharing objects by pointer using a reference count.
/// It has members containing a pointer to an object and a reference count.
/// Sharing is done by copying SharedPtr objects.
/// Copying is fast since SharedPtr objects are small.
/// Unsharing a count is done by deleting a SharedPtr object.
/// Only when the last SharedPtr is deleted (the original or the last copy)
/// will the actual object be deleted.
///
/// Usage:
/// ------
/// Sharing an object is done by copying SharedPtr objects.
/// Copying a SharedPtr increments a reference count of the object being shared.
///
///     SharedPtr<Object> shared1( new Object );
///     SharedPtr<Object> shared2 = shared1;  // copy constructor
///
/// Passing a SharedPtr to a function is implicit copying,
/// so the caller and callee shares the object.
/// A SharedPtr should be passed by value (not as a pointer/reference):
///
///     void Func( SharedPtr<Object> obj );   // right
///     void Func( SharedPtr<Object>& obj );  // wrong, inhibits necessary copying method
///
/// Directly assigning a SharedPtr with a pointer unshares one count of the outgoing object,
/// and resets the SharedPtr's reference count to 1 for the incoming object.
///
///     SharedPtr<Object> ptr1( new Object );
///     SharedPtr<Object> ptr2 = ptr1;      // ptr1 and ptr2 share one Object (refCnt=2)
///     ptr2 = new Object;                  // ptr2 points to different Object (refCnt=1, refCnt=1)
///
/// Examples:
/// ---------
/// Instead of copying a 1MB pixmap requiring explicit memory management...
///
///     class Pixmap
///     {
///         uint8* GetBuf();
///     };
///     
///     class Object
///     {
///        Object( Pixmap& pixmap )
///        {
///            mBuf = new uint8[pixmap.GetBufLen()];  // new array
///            memcpy( mBuf, pixmap.GetBuf(), pixmap.GetBufLen() );  // copy array
///        }
///        ~Object()
///        {
///            delete[] mBuf;  // free array
///        }
///        uint8* mBuf;
///     };
///
/// ...using SharedPtr is way faster and simpler/safer as delete is eliminated:
///
///     class Pixmap
///     {
///         SharedPtrArr<uint8> GetBuf();  // shared array
///     };
///
///     class Object
///     {
///        Object( Pixmap& pixmap )
///        {
///            mBuf = pixmap.GetBuf();  // SharedPtrArr assignment is fast
///        }
///        ~Object()  // dtor now does nothing
///        {
///        }
///        SharedPtrArr<uint8> mBuf;  // shared array
///     };
///
/// Caveats/Warnings:
/// -----------------
/// Clients must never explicitly delete the shared object.
///
/// @endverbatim
///
/// @class gfx::SharedPtrArr
/// @brief Shared arrays using a reference-count.
///
/// "SharedPtrArr" is a long name but it was chosen instead of "SharedArr"
/// to indicate its similarity to SharedPtr and contrast it against SharedVal.
/// See gfx::SharedPtr.
////////////////////////////////////////////////////////////////////////////////

// (Not enclosed by #ifndef as it is re-included by gfx_shared_ptr.hh)

namespace gfx {

////////////////////////////////////////////////////////////////////////////////
//                 SharedPtr( void );
//                 SharedPtr( T* obj );
//                 SharedPtr( const SharedPtr<T>& src );
//                 ~SharedPtr();
// SharedPtr<T>&   operator=( const SharedPtr<T>& src );
// SharedPtr<T>&   operator=( T* obj );
// T*              operator->( void ) const;
// T&              operator*( void ) const;
// bool            operator==( const SharedPtr& other ) const;
// bool            operator!=( const SharedPtr& other ) const;
// bool            operator==( const T* obj ) const;
// bool            operator!=( const T* obj ) const;
//
template<typename T>
class SharedPtr
{
public:
    // Default ctor constructs an invalid SharedPtr.
    SharedPtr( void )
    {
        Share1( NULL );  // reference count = 1
    }

public:
    // Constructor.  Initially share one count of an actual object.
    SharedPtr( T* obj )
    {
        Share1( obj );  // reference count = 1
    }

public:
    // Copy constructor.  Increment reference count of shared object.
    SharedPtr( const SharedPtr<T>& src )
    {
        // Just copy.  This is a new object so it has nothing to unshare.
        Copy( src );
    }

public:
    // Destructor.
    ~SharedPtr()
    {
        Unshare();
        DEBUG_CODE( mObj = NULL; mRefCntPtr = NULL; )
    }

public:
    // Copy a SharedPtr into another.  Copying shares an object.
    SharedPtr<T>& operator=( const SharedPtr<T>& src )
    {
        // Self-assignment is unexpected but not handling it would be folly.
        if ( EX( &src != this ) )
        {
            // Unshare this destination before reassigning it.
            Unshare();
            Copy( src );
        }
        return *this;
    }

public:
    // Reassign this SharedPtr with a different pointer.
    // Unshare the outgoing pointer, and reset the reference count to 1
    // for the incoming pointer.
    SharedPtr<T>& operator=( T* obj )
    {
        // Unshare() might delete mRefCntPtr, Share1() will make a new one.
        Unshare();      // unshare outgoing pointer
        Share1( obj );  // assign with incoming pointer, reset reference count = 1
        return *this;
    }

public:
    T*   operator->( void ) const { return mObj; }
    T&   operator*( void ) const { return *mObj; }  // dereference, not multiply
    bool operator==( const SharedPtr& other ) const { return mObj == other.mObj; }
    bool operator!=( const SharedPtr& other ) const { return mObj != other.mObj; }
    bool operator==( const T* obj ) const { return mObj == obj; }
    bool operator!=( const T* obj ) const { return mObj != obj; }

private:
    // Directly copy a SharedPtr object.
    // @remarks Logically, the source should be valid.  However, in practice,
    // vector<SharedPtr>.resize() invokes the copy constructor, though you'd
    // think the STL would use only the default constructor.
    void Copy( const SharedPtr<T>& src )
    {
        // Increment the source's reference count as this destination is a copy.
        mObj        = src.mObj;
        mRefCntPtr  = src.mRefCntPtr;
        ++(*src.mRefCntPtr);
    }

private:
    // Initialize/reset sharing with reference count = 1.
    void Share1( T* obj )
    {
        mObj        = obj;
        mRefCntPtr  = new int;          // reference count isn't tied to this object
        *mRefCntPtr = 1;                // initially one
    }

private:
    // Decrease reference count.
    void Unshare( void )
    {
        // Default constructor creates a SharedPtr that points to nothing.
        // Decrement the reference count.
        // Don't delete shared resources until it hits zero.
        if ( --(*mRefCntPtr) == 0 )
        {
            SharedPtrDeleteObj( mObj );  // macro for delete or delete[]
#if ! SPEED
            DELETE_NULL( mRefCntPtr );   // for speed, let it leak a measly int
#endif
        }
    }

private:
    T*          mObj;           ///< the actual resource to share
    int*        mRefCntPtr;     ///< reference count isn't tied to this object

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

// Restricted usage.
template<typename T>       T*       GET_PTR( const SharedPtr<T> ptr ) { return ptr.mObj; }
template<typename T> const T* GET_CONST_PTR( const SharedPtr<T> ptr ) { return ptr.mObj; }

} // namespace gfx
