/****************************************************************************************

		MCCountedPtrHelper.h

		Copyright: (c) 2001 Eovia Corp. All rights reserved.

		Author:	Charles
		Date:	06/25/99

		Revision History

		when		who			what & why
		==========	==========	======================================================
		9/2/99		Ian			Added support for the new types of add stack reference
								and for the CountedPtrContext. See MCCountedPtr.cpp & .h
								for more information.
					
*****************************************************************************************/

#ifndef __MCCOUNTEDPTRHELPER__
#define __MCCOUNTEDPTRHELPER__

#if CP_PRAGMA_ONCE
#pragma once
#endif

#include "MCCountedPtr.h"


/// \addtogroup MCCore
/// \{

///	<P>Counted objects are objects that use a counter to keep track of how many other objects need them, 
/// and delete themselves when nobody needs them anymore. A counted object has an AddRef method that increments its counter, 
/// and a Release method that decrements it. When the value of the counter reaches zero, the object is deleted.</P>
///
/// <P>It is possible to call AddRef and Release "by hand" but it is a bad idea for at least two reasons. 
/// First, it is very easy to forget to add a call to Release, therefore causing a memory leak.
/// Second the code becomes more complex because in every function that calls AddRef, you must consider exceptions.
/// So you have to use a try/catch block, add a call to Release at the end of the try AND another one 
/// before re-throwing in the catch section...</P>
///
///	<P>Because they automatically call AddRef and Release, TMCCountedPtr, TMCCountedGetHelper 
/// and TMCCountedCreateHelper are your best friends.</P>
/// 
/// <P>TMCCountedGetHelper makes it easier to return a pointer to a counted object through a function's parameter. 
/// Here's a typical code sample:</P>
/// \code
/// class TMCCountedGetHelperSample
/// { 
///	public:
///		void GetMyFile(IMCFile** outFile)
///		{
///			TMCCountedGetHelper<IMCFile> result(outFile);	//releases *outFile if its not null
///			result= fMyPreciousFile;						//puts fMyPreciousFile into *outFile and addrefs it (if its not null)
///		}
/// protected:
///		TMCCountedPtr<IMCFile> fMyPreciousFile;
///	}
/// \endcode
///
/// For more information, please refer to the "Counted Pointers" chapter in the overview of the SDK.
///
template<class T> class TMCCountedGetHelper : public TMCObject
{
	public:
		TMCCountedGetHelper		(T** ptr) : fPtr(ptr) {ReleaseIfNotNull<T>(ptr);}
		TMCCountedGetHelper		(void** ptr) : fPtr(reinterpret_cast<T**>(ptr)) {ReleaseIfNotNull(*ptr);}
		~TMCCountedGetHelper	() {}

		T*& operator= (T* ptr) const
		{
			if (*fPtr)
			{
#if Debug_ && qRecordCountedPtrRefs
				if(macroIsCountedPtr(fPtr))
				{
					RemoveCPStackReference<T>(
						*fPtr,
						macroRetreiveCPContext(fPtr));
				}
				else
				{
					RemovePStackReference<T>(*fPtr, fPtr);
				}
#endif
				(*fPtr)->Release();
			}
			*fPtr = ptr;
			if (*fPtr)
			{
				(*fPtr)->AddRef();
#if Debug_ && qRecordCountedPtrRefs
				if(macroIsCountedPtr(fPtr))
				{
					AddCPStackReference<T>(
						*fPtr,
						macroRetreiveCPContext(fPtr));
				}
				else
				{
					AddPStackReference<T>(*fPtr, fPtr);
				}
#endif
			}
			return *fPtr;
		}
		T* operator-> () const				{return *fPtr;}
		operator T*() const					{return *fPtr;}
		T& operator* () const				{return **fPtr;}
	protected:
		T**									fPtr;
};

/// \}

/// \addtogroup MCCore
/// \{

///	<P>Counted objects are objects that use a counter to keep track of how many other objects need them, 
/// and delete themselves when nobody needs them anymore. A counted object has an AddRef method that increments its counter, 
/// and a Release method that decrements it. When the value of the counter reaches zero, the object is deleted.</P>
///
/// <P>It is possible to call AddRef and Release "by hand" but it is a bad idea for at least two reasons. 
/// First, it is very easy to forget to add a call to Release, therefore causing a memory leak.
/// Second the code becomes more complex because in every function that calls AddRef, you must consider exceptions.
/// So you have to use a try/catch block, add a call to Release at the end of the try AND another one 
/// before re-throwing in the catch section...</P>
///
///	<P>Because they automatically call AddRef and Release, TMCCountedPtr, TMCCountedGetHelper 
/// and TMCCountedCreateHelper are your best friends.</P>
/// 
/// <P>TMCCountedCreateHelper makes it easier implement a function that creates a counted object and 
/// returns it through a parameter. Here's a typical code sample:</P>
///
/// \code
/// class TCreateHelperSample : public TMCSMPCountedObject
/// { 
///	public:
///		static void Create(TCreateHelperSample** outSample)
///		{
///			TMCCountedCreateHelper<TCreateHelperSample> result(outSample);	//releases *outSample if its not null
///
///			//Puts the new sample into *outSample but does NOT addref it (the refcount is initialized to 1)
///			result= new TCreateHelperSample();	
///		}
/// protected:
///		TCreateHelperSample() {}
///		~TCreateHelperSample() {}
///	}
/// \endcode
///
///	\note that in such a case, you must use a TMCCountedCreateHelper. 
/// If you were using a TMCCountedGetHelper, the object would never be deleted because of the extra addref.
///
/// For more information, please refer to the "Counted Pointers" chapter in the overview of the SDK.
///
template<class T> class TMCCountedCreateHelper : public TMCObject
{
	public:
		TMCCountedCreateHelper	(T** ptr) : fPtr(ptr) {ReleaseIfNotNull<T>(ptr);}
		TMCCountedCreateHelper	(void** ptr) : fPtr(reinterpret_cast<T**>(ptr)) {ReleaseIfNotNull(*ptr);}
		~TMCCountedCreateHelper	() {}
		T*& operator= (T* ptr) const
		{
			ThrowIfNoMem(ptr);
			if (*fPtr)
			{
#if Debug_ && qRecordCountedPtrRefs
				if(macroIsCountedPtr(fPtr))
				{
					RemoveCPStackReference<T>(
						*fPtr,
						macroRetreiveCPContext(fPtr));
				}
				else
				{
					RemovePStackReference<T>(*fPtr, fPtr);
				}
#endif
				(*fPtr)->Release();
			}
			*fPtr = ptr;
			if (*fPtr)
			{
#if Debug_ && qRecordCountedPtrRefs
				if(macroIsCountedPtr(fPtr))
				{
					AddCPStackReference<T>(
						*fPtr,
						macroRetreiveCPContext(fPtr));
				}
				else
				{
					AddPStackReference<T>(*fPtr, fPtr);
				}
#endif
			}
			return *fPtr;
		}
		T* operator->() const				{return *fPtr;}
		operator T*() const					{return *fPtr;}
		T& operator* () const				{return **fPtr;}
	protected:
		T**									fPtr;
};
/// \}

/// Deprecated, use MCCountedCreateHelper instead
template<class T> class TMCCountedCreateHelperNoThrow : public TMCObject
{
	public:
		TMCCountedCreateHelperNoThrow(T** ptr) : fPtr(ptr) {ReleaseIfNotNull<T>(ptr);}
		TMCCountedCreateHelperNoThrow(void** ptr) : fPtr(reinterpret_cast<T**>(ptr)) {ReleaseIfNotNull(*ptr);}
		~TMCCountedCreateHelperNoThrow() {}
		T*& operator= (T* ptr) const
		{
			if (*fPtr)
			{
#if Debug_ && qRecordCountedPtrRefs
				if(macroIsCountedPtr(fPtr))
				{
					RemoveCPStackReference<T>(
						*fPtr,
						macroRetreiveCPContext(fPtr));
				}
				else
				{
					RemovePStackReference<T>(*fPtr, fPtr);
				}
#endif
				(*fPtr)->Release();
			}
			*fPtr = ptr;
			if (*fPtr)
			{
#if Debug_ && qRecordCountedPtrRefs
				if(macroIsCountedPtr(fPtr))
				{
					AddCPStackReference<T>(
						*fPtr,
						macroRetreiveCPContext(fPtr));
				}
				else
				{
					AddPStackReference<T>(*fPtr, fPtr);
				}
#endif
			}
			return *fPtr;
		}
		T* operator ->() const				{return *fPtr;}
		operator T*() const					{return *fPtr;}
		T &operator *() const				{return **fPtr;}
	protected:
		T**									fPtr;
};

//for backward compatibility
#define TRefAddingPtr TMCCountedGetHelper

#endif