/*****************************************************************************
 * $LastChangedDate: 2010-05-25 21:26:05 -0400 (Tue, 25 May 2010) $
 * @file
 * @author  Jim E. Brooks  http://www.palomino3d.org
 * @brief   Reference-counted pointer template classes (documentation).
 *//*
 * LEGAL:   COPYRIGHT (C) 2007 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE VERSION 2 (GPL 2).
 *****************************************************************************/

// Long documents put here to speed compiling.
// This contains two sections for doxygen and text documention.

#if DOXYGEN

// =============================================================================
// Doxygen section.
// =============================================================================

namespace base {
////////////////////////////////////////////////////////////////////////////////
/// @brief Shared is a base class with an intrusive reference-count maintained by shptr/shptr0.
///
/// See shptr_doc.hh.
///
/// Shared isn't thread-safe.  For threading, use ThreadableShared with thptr or thptr0.
///
/// Shared provides storage for the reference-count that shptr maintains.
/// Combining the reference-count with the object reduces memory allocations
/// (as opposed to allocating a separate reference-count).
/// Shared is just a data class.  shptr computes the reference-count.
///
/// @verbatim
/// Example:
/// class Object : public Shared { ... };
/// shptr<Object> object = new Object;
/// @endverbatim
///
class Shared : public threads::NonThreadable { };
}  // namespace base

namespace threads {
////////////////////////////////////////////////////////////////////////////////
/// @brief ThreadableShared is a thread-safe base class with an intrusive reference-count maintained by thptr/thptr0.
///
/// See shptr_doc.hh.
///
/// thptr (threadable smart-pointer) locks the reference-count.
///
/// @verbatim
/// Example:
/// class Object : public ThreadableShared { ... };
/// thptr<Object> object = new Object;
/// @endverbatim
///
class ThreadableShared : public threads::Threadable { };
} // namespace threads

namespace base {
////////////////////////////////////////////////////////////////////////////////
/// @brief shptr is a smart pointer that maintains a reference-count of Shared objects.
///
/// See shptr_doc.hh
///
/// @remarks CANNOT BE NULL!
///
template<class SHARED> class shptr : public threads::NonThreadable { };
}  // namespace base

namespace base {
////////////////////////////////////////////////////////////////////////////////
/// @brief shptr0 is a nullable smart pointer that maintains a reference-count of Shared objects.
///
/// See shptr_doc.hh
///
/// @remarks Can be assigned NULL but using it (and NULL) is discouraged.  shptr0 is slower than shptr.
///
template<class SHARED> class shptr0 : public threads::NonThreadable { };
} // namespace base

namespace threads {
////////////////////////////////////////////////////////////////////////////////
/// @brief thptr is a thread-safe variant of shptr with a lock.
///
/// thptr (threadable smart-pointer) locks every method of ThreadableShared.
///
/// See shptr_doc.hh
///
/// @verbatim
/// Example:
/// class Object : public ThreadableShared { ... };
/// thptr<Object> object = new Object;
/// @endverbatim
///
template<class SHARED> class thptr : public threads::Threadable { };
} // namespace threads

namespace threads {
////////////////////////////////////////////////////////////////////////////////
/// @brief thptr0 is a thread-safe variant of the nullable shptr0 with a lock.
///
/// thptr0 (threadable nullable smart-pointer) locks the reference-count of ThreadableShared.
///
/// See shptr_doc.hh
///
template<class SHARED> class thptr0 : public threads::Threadable { };
} // namespace threads

namespace base {
////////////////////////////////////////////////////////////////////////////////
/// @brief RefPtr is an alias of osg::ref_ptr for OSG objects.
///
/// @remarks osg::ref_ptr IS NON-THREADABLE!  Use the faster shptr for non-OSG objects.
///
template<class REFERENCED> class RefPtr : public threads::NonThreadable { };
}  // namespace base

// =============================================================================
// Section for documentation of shared smart pointers.
// =============================================================================

//------------------------------------------------------------------------------
//
// ##########################################################
// If NULL pointer is unavoidable, use shptr0 (note the '0').
// Regular shptr will malfunction if given NULL.
// ##########################################################
//
// Interface:
// ----------
//             shptr( void );  // default ctor constructs a new T
//             shptr0( void);  // -or- default ctor of shptr0 constructs NULL pointer
//             shptr( T* obj );
//             shptr( const shptr<T>& src );
//             ~shptr();
// shptr<T>&   operator=( const shptr<T>& src );
// shptr<T>&   operator=( T* obj );
// T*          operator->( void ) const;
// T&          operator*( void ) const;
// bool        operator==( const shptr& other ) const;
// bool        operator!=( const shptr& other ) const;
// bool        operator==( const T* obj ) const;
// bool        operator!=( const T* obj ) const;
// T*          PTR();
// T&          REF();
// const T*    CONST_PTR();
// const T&    CONST_REF();
//
// Overview:
// ---------
// Shared is a base class that provides storage for a reference-count.
// Shared itself is a NOP.  Rather, objects to be reference-counted
// are given to a shptr which maintains the reference-count.
//
// shptr will own the object and is responsible for deleting the object.
// Sharing an object is done by copying a shptr which increments
// the reference-count (in the Shared base part of the object).
// When a shptr is deleted, ~shptr() decrements the reference-count
// and deletes the object when the count falls to zero.
//
// Usage:
// ------
// Sharing an object is done by copying shptr objects.
// Copying a shptr increments a reference count of the object being shared.
//
//     shptr<Object> shared1( new Object );
//     shptr<Object> shared2 = shared1;  // copy constructor
//
// Passing a shptr to a function is implicit copying,
// so the caller and callee shares the object.
// A shptr should be passed by value (not as a pointer/reference):
//
//     void Func( shptr<Object> obj );   // right
//     void Func( shptr<Object>& obj );  // wrong, inhibits necessary copying method
//
// Directly assigning a shptr with a pointer unshares one count of the outgoing object,
// and resets the shptr's reference count to 1 for the incoming object.
//
//     shptr<Object> ptr1( new Object );
//     shptr<Object> ptr2 = ptr1;      // ptr1 and ptr2 share one Object (refCount=2)
//     ptr2 = new Object;              // ptr2 points to different Object (refCount=1, refCount=1)
//
// const shptr example:
//
//     shptr<const Object> objc = new Object;
//     shptr<Object> obj = new Object;
//     objc = obj;  // ok (unless const object is supposed to be undeletable)
//     obj = objc;  // won't compile
//
// shptr can effectively transfer ownership to caller:
//
//     shptr<Object>
//     Producer( void )
//     {
//         shptr<Object> obj = new Object;
//         obj->AddData(..);
//         ...
//         return obj;
//     }
//
// A Shared object can be converted to a raw pointer
// and then converted back to a shptr variable.
// This is very useful for a Visitor pattern which ordinarily
// visits nodes in terms of raw pointers for speed, but rarely
// needs to retain a reference-counted pointer to a node.
//
// class Node : public Shared { };
//
// class Visitor
// {
//     void Visit( Node& node )
//     {
//         if ( node == sought )
//         {
//             // Can be thought of as a conversion from Node* to shptr<Node>.
//             // Actually invokes shptr::operator=(T*) which increases refCount.
//             mMatch = &node;
//         }
//     }
//
// private:
//     shptr<Node>    mMatch;
// };
//
// 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 shptr is way faster and simpler/safer as delete is eliminated:
//
//     class Pixmap
//     {
//         shptr< Array<uint8> > GetBuf();  // shared array
//     };
//
//     class Object
//     {
//        Object( Pixmap& pixmap )
//        {
//            mBuf = pixmap.GetBuf();  // shptrArr assignment is fast
//        }
//        ~Object()  // dtor now does nothing
//        {
//        }
//        shptr< Array<uint8> > mBuf;  // shared array
//     };
//
// ...if a class has protected ctors/dtors then declare shptr a friend:
//
//     class Derived : public Object
//     {
//     friend class shptr<Derived>;
//     protected:
//         Derived( void );
//     };
//
// An individual Shared object is interchangable between the variants of shptr.
// --------------------------------------------------------------------------------
// The Shared object is independent of the shptr that encapsulates it.
// By using PTR(), a Shared can be extract from one kind of shptr
// and exchanged with another kind of shptr.
//
// shptr<Data> data = new Data;
// Producer producer( data.PTR() );
// class Producer
// {
//     Producer( shptr0<Data> data );  // Shared interchanged as shptr0
// };
//
// shptr0 (nullable shptr):
// ------------------------
// Regular shptr must be optimal since it is very frequent.
// If a NULL shptr is possible and unavoidable, use the slower shptr0.
// The same Shared object can be interchanged in a shptr or shptr0
// (as long as the value isn't NULL at run-time of course).
//
// shptr0 can point to abstract objects (unlike shptr which
// requires a default ctor which abstract classes lack).
//
// An example of an unavoidable case where shptr has to be NULL (interimly):
// class Font
// {
//   //shptr<Texture>   mTexture;  // WRONG!  shptr ctor will crash!
//     shptr0<Texture>  mTexture;  // ok, shptr0 can handle NULL
// };
// Font::Font( const FontDesc& desc, SafePtr<Gui> gui )
// :   mTexture(NULL)
// {
//     [load .tga file]
//     mTexture = new Texture( targa, Texture::eMipmap_ON );
// }
//
// Rules and pitfalls:
// -------------------
// - Ways affecting the reference-count:
//
//   - passed as an arg
//   - returned
//   - constructed/destructed as a class member
//   - reassigned with a new object or another shptr (src shptr affected too)
//   - copied directly
//   - copied indirectly (as a member of a copyable class, container element, etc)
//
// - Be aware that once a Shared object is encapsulated in a shptr,
//   it becomes subject to automatic deletion by shptr.
//
// - Transferring ownership vs. sharing ownership:
//
//   shptr is usually for sharing ownership, but it can be used to transfer ownership.
//
//   class Object : public Shared
//   {
//       // share ownership:
//       shptr<Buffer>  GetBuffer( void ) { return mBuffer; }
//       shptr<Buffer>  mBuffer;
//
//       // transfer ownership:
//       shptr<Buffer>  GetBuffer( void ) { return new Buffer; }
//
//       // WRONG/BUG (see below):
//       shptr<Buffer>  GetBuffer( void ) { return mBuffer; }
//       Buffer*            mBuffer;
//   };
//
// - PITFALL: A Shared, as a class member, must be encapsulated in shptr:
//
//   Otherwise, as an actual member or a raw pointer, a Shared object
//   will initially have a zero reference-count.  But the the class instance
//   implicitly references it.  Any accessor method in the class that
//   returns it as a shptr can may prematurely destroy it.
//   Though not recommended, if a Shared needs to be an actual member,
//   the class ctor can use Shared::IncRefCount() to account for its reference.
//
//   class Node : public Shared
//   {
//   };
//   class Graph
//   {
//       shptr<Node>  GetRoot( void ) { return mRoot; }
//
//       Node*        mRoot;  // WRONG
//       Node         mRoot;  // WRONG
//       shptr<Node>  mRoot;  // ok
//   };
//
// - Don't blindly change T* to shptr<T>:
//
//   Buffer sBuffer;
//   < Buffer* GetBuffer( void )        // original code
//   > shptr<Buffer> GetBuffer( void )  // revised code
//   {
//       return sBuffer;  // oops: implicitly converted to shptr and deleted!
//   }
//
// - Pitfalls in replacing raw pointers with shptr:
//
//   Object* mObject;
//   shptr<Object> mObject;
//   delete mObject; mObject = new Object;  // renew idiom
//
//   shptr::operator=() will malfunction by try freeing the deleted mObject.
//   Correction is to reassign shptr without deleting.
//
//   shptr<Object> mObject;
//   mObject = new Object;  // reassign shptr
//
// Design notes:
// -------------
// Disadvantages are that shptr intrusively requires deriving from Shared
// and wastes an int in objects that aren't actually shared.
// But the advantage is speed: the reference-count is allocated along with
// the object, as opposed to having a separately allocated reference-count.
// This advantage becomes important when lots of small objects are shared
// and frequently created.
//
// Implementation (shptr code):
// ----------------------------
// - SHARED constructs a new object with initially mRefCount=0.
//   Object* pObj = new Object;   // mRefCount=0
//   shptr<Object> shObj = pObj;  // mRefCount=1
//   Therefore, mRefCount=0 is NOT an accurate indication of a destroyed SHARED.
// - SHPTR_CHECK() can catch reviving a destroyed SHARED by checking its typesig.
//
// Implementation (Shared class):
// ------------------------------
// - Clients can just derive using "public Shared" since mRefCount is private.
//   "private Shared" is ideal but every client would have to declare shptr a friend.
// - IncRefCount():
//   LIMITED USE: A special case is where a class has a Shared as an
//   actual member but has methods that return it as a shptr.
//   The class implicitly hold a reference to it, so ctor should increment refCount.
//
// Threading and locking Shared objects, thptr/thptr0:
// ---------------------------------------------------
// For threading, use the classes ThreadableShared (the refCount)
// with thptr/thptr0 (the smart pointer that locks the refCount).
//
// ThreadableShared objects are guarded by a global recursive lock (FastLock).
// Before attempting to use a finer-granularity lock,see doc/shptr.txt
// for a full explanation of this design decision.
//
//------------------------------------------------------------------------------
//
// shptr vs osg::ref_ptr:
// ----------------------
// Two different smart-pointers are used: shptr and RefPtr (osg::ref_ptr).
// shptr is faster, does memory-checking, and has thread-safe locked variants.
// RefPtr is an abbreviation for osg::ref_ptr which is for OSG objects.
// Confusing them is easy but compiler will catch that.
//
//------------------------------------------------------------------------------

#endif // DOXYGEN
