/*=============================================================================
 *  CAAutoDisposer.h
 *  CAServices
 *
 *  Created by James McCartney on 10/12/05.
 *  Copyright 2005 Apple Computer. All rights reserved.
 *
 * CAAutoFree is a simple auto_ptr-like smart pointer template for automatically freeing malloced pointers when they go out of scope.
 * std::auto_ptr handles a single C++ object. In CoreAudio we are often working with malloced arrays of "plain old data" types.
 * Using this class eliminates memory leaks even when exceptions get thrown.
 * This class has the same ownership passing semantics that auto_ptr does.
 * If you pass a CAAutoFree by value to a function you are passing ownership of the pointer to that function. (The contained pointer
 * in the caller will be NULL after passing the CAAutoFree.)
 * If you return a CAAutoFree by value from a function you are passing ownership to the caller.
 * If you pass a CAAutoFree const reference  (CAAutoFree<T> const&) to a function then the function can use the pointer, 
 * but does not take ownership and the pointer remains valid. 
 *
 *
 * CAAutoArrayDelete is for holding arrays of new'ed C++ objects. Bjarne Stroustrup says that "auto_array_ptr" isn't 
 * necessary and recommends using a vector. But vector is rather heavy when all I need is a simple smart pointer. 
 * And vectors have slightly different semantics when passing them as const references than a smart pointer does.
 *
 * CAAutoDelete is essentially the same as auto_ptr
 * 
	$Log: CAAutoDisposer.h,v $
	Revision 1.7  2005/12/13 02:46:29  asynth
	add alloc method for CAAutoArrayDelete
	
	Revision 1.6  2005/11/29 23:38:53  asynth
	operator[] and operator+ were not compiling on windows and aren't necessary anyway.
	
	Revision 1.5  2005/10/24 22:21:08  asynth
	throw bad_alloc
	
	Revision 1.4  2005/10/21 23:32:56  asynth
	CAPtr -> CAAutoFree
	
	Revision 1.13  2005/10/20 23:16:00  asynth
	tweaks
	
	Revision 1.12  2005/10/17 23:01:41  asynth
	another C++ wrinkle: a template constructor is never a copy constructor
	
	Revision 1.11  2005/10/17 19:01:15  asynth
	have to reassert operator=, else it gets hidden by the default implementation
	
	Revision 1.10  2005/10/15 02:20:06  asynth
	add CAOPtr.
	
	Revision 1.9  2005/10/13 19:02:15  asynth
	change get() to operator()
	
	Revision 1.8  2005/10/13 18:19:48  asynth
	fix comment
	
	Revision 1.7  2005/10/13 18:18:19  asynth
	fix comment
	
	Revision 1.6  2005/10/13 18:08:34  asynth
	remove no-throw exception specs
	
	Revision 1.5  2005/10/13 01:36:28  asynth
	add operators + []
	
	Revision 1.4  2005/10/13 00:18:01  asynth
	safe const conversion. example program
	
	Revision 1.3  2005/10/12 23:33:31  asynth
	better release, copy constructor
	
	Revision 1.2  2005/10/12 22:13:47  asynth
	use size_t
	
	Revision 1.1  2005/10/12 22:10:32  asynth
	add smart pointer class
	

	$NoKeywords: $
 =============================================================================*/

#if !defined(__CAPtr_h__)
#define __CAPtr_h__

#include <stdlib.h>		// for malloc, calloc
#include <new>			// for bad_alloc

// helper class for automatic conversions
template <typename T>
struct CAPtrRef
{
	T* ptr_;

	explicit CAPtrRef(T* ptr) : ptr_(ptr) {}
};

template <typename T>
class CAAutoFree
{
private:
	T* ptr_;

public:
	
	CAAutoFree() : ptr_(0) {}
	
	explicit CAAutoFree(T* ptr) : ptr_(ptr) {}
	
	template<typename U>
	CAAutoFree(CAAutoFree<U>& that) : ptr_(that.release()) {} // take ownership

	// C++ std says: a template constructor is never a copy constructor
	CAAutoFree(CAAutoFree<T>& that) : ptr_(that.release()) {} // take ownership

	CAAutoFree(size_t n, bool clear = false) 
		// this becomes an ambiguous call if n == 0
		: ptr_(static_cast<T*>(clear ? calloc(n, sizeof(T)) : malloc(n * sizeof(T)))) 
		{
			if (!ptr_) 
				throw std::bad_alloc();
		}
	
	~CAAutoFree() { free(); }
	
	void alloc(size_t numItems, bool clear = false) 
	{
		free();
		ptr_ = static_cast<T*>(clear ? calloc(numItems, sizeof(T)) : malloc(numItems * sizeof(T)));
		if (!ptr_) 
			throw std::bad_alloc();
	}
	
	void allocBytes(size_t numBytes, bool clear = false) 
	{
		free();
		ptr_ = static_cast<T*>(clear ? calloc(1, numBytes) : malloc(numBytes));
		if (!ptr_) 
			throw std::bad_alloc();
	}
	
	template <typename U>
	CAAutoFree& operator=(CAAutoFree<U>& that) 
	{ 
		set(that.release());	// take ownership
		return *this;
	}
	
	CAAutoFree& operator=(CAAutoFree& that) 
	{ 
		set(that.release());	// take ownership
		return *this;
	}
	
	CAAutoFree& operator=(T* ptr) 
	{
		set(ptr); 
		return *this;
	}
	
	template <typename U>
	CAAutoFree& operator=(U* ptr) 
	{
		set(ptr); 
		return *this;
	}
		
	T& operator*() const { return *ptr_; }
	T* operator->() const { return ptr_; }
	
	T* operator()() const { return ptr_; }
	T* get() const { return ptr_; }
	operator T*() const { return ptr_; }

	bool operator==(CAAutoFree const& that) const { return ptr_ == that.ptr_; }
	bool operator!=(CAAutoFree const& that) const { return ptr_ != that.ptr_; }
	bool operator==(T* ptr) const { return ptr_ == ptr; }
	bool operator!=(T* ptr) const { return ptr_ != ptr; }
		
	T* release() 
	{
		// release ownership
		T* result = ptr_;
		ptr_ = 0;
		return result;
	}
	
	void set(T* ptr)
	{
		if (ptr != ptr_)
		{
			::free(ptr_);
			ptr_ = ptr;
		}
	}
	
	void free() 
	{
		set(0);
	}


	// automatic conversions to allow assignment from results of functions.
	// hard to explain. see auto_ptr implementation and/or Josuttis' STL book.
	CAAutoFree(CAPtrRef<T> ref) : ptr_(ref.ptr_) { }

	CAAutoFree& operator=(CAPtrRef<T> ref)
	{
		set(ref.ptr_);
		return *this;
	}

	template<typename U>
	operator CAPtrRef<U>()
		{ return CAPtrRef<U>(release()); }

	template<typename U>
	operator CAAutoFree<U>()
		{ return CAAutoFree<U>(release()); }
	
};


template <typename T>
class CAAutoDelete
{
private:
	T* ptr_;

public:
	CAAutoDelete() : ptr_(0) {}

	explicit CAAutoDelete(T* ptr) : ptr_(ptr) {}
	
	template<typename U>
	CAAutoDelete(CAAutoDelete<U>& that) : ptr_(that.release()) {} // take ownership

	// C++ std says: a template constructor is never a copy constructor
	CAAutoDelete(CAAutoDelete<T>& that) : ptr_(that.release()) {} // take ownership
	
	~CAAutoDelete() { free(); }
	
	template <typename U>
	CAAutoDelete& operator=(CAAutoDelete<U>& that) 
	{ 
		set(that.release());	// take ownership
		return *this;
	}
	
	CAAutoDelete& operator=(CAAutoDelete& that) 
	{ 
		set(that.release());	// take ownership
		return *this;
	}
	
	CAAutoDelete& operator=(T* ptr) 
	{
		set(ptr); 
		return *this;
	}
	
	template <typename U>
	CAAutoDelete& operator=(U* ptr) 
	{
		set(ptr); 
		return *this;
	}
		
	T& operator*() const { return *ptr_; }
	T* operator->() const { return ptr_; }
	
	T* operator()() const { return ptr_; }
	T* get() const { return ptr_; }
	operator T*() const { return ptr_; }
	
	bool operator==(CAAutoDelete const& that) const { return ptr_ == that.ptr_; }
	bool operator!=(CAAutoDelete const& that) const { return ptr_ != that.ptr_; }
	bool operator==(T* ptr) const { return ptr_ == ptr; }
	bool operator!=(T* ptr) const { return ptr_ != ptr; }
		
	T* release() 
	{
		// release ownership
		T* result = ptr_;
		ptr_ = 0;
		return result;
	}
	
	void set(T* ptr)
	{
		if (ptr != ptr_)
		{
			delete ptr_;
			ptr_ = ptr;
		}
	}
	
	void free() 
	{
		set(0);
	}


	// automatic conversions to allow assignment from results of functions.
	// hard to explain. see auto_ptr implementation and/or Josuttis' STL book.
	CAAutoDelete(CAPtrRef<T> ref) : ptr_(ref.ptr_) { }

	CAAutoDelete& operator=(CAPtrRef<T> ref)
	{
		set(ref.ptr_);
		return *this;
	}

	template<typename U>
	operator CAPtrRef<U>()
		{ return CAPtrRef<U>(release()); }

	template<typename U>
	operator CAAutoFree<U>()
		{ return CAAutoFree<U>(release()); }
	
};


template <typename T>
class CAAutoArrayDelete
{
private:
	T* ptr_;

public:
	CAAutoArrayDelete() : ptr_(0) {}

	explicit CAAutoArrayDelete(T* ptr) : ptr_(ptr) {}
	
	template<typename U>
	CAAutoArrayDelete(CAAutoArrayDelete<U>& that) : ptr_(that.release()) {} // take ownership

	// C++ std says: a template constructor is never a copy constructor
	CAAutoArrayDelete(CAAutoArrayDelete<T>& that) : ptr_(that.release()) {} // take ownership
	
	// this becomes an ambiguous call if n == 0
	CAAutoArrayDelete(size_t n) : ptr_(new T[n]) {}
	
	~CAAutoArrayDelete() { free(); }
	
	void alloc(size_t numItems) 
	{
		free();
		ptr_ = new T [numItems];
	}
	
	template <typename U>
	CAAutoArrayDelete& operator=(CAAutoArrayDelete<U>& that) 
	{ 
		set(that.release());	// take ownership
		return *this;
	}
	
	CAAutoArrayDelete& operator=(CAAutoArrayDelete& that) 
	{ 
		set(that.release());	// take ownership
		return *this;
	}
	
	CAAutoArrayDelete& operator=(T* ptr) 
	{
		set(ptr); 
		return *this;
	}
	
	template <typename U>
	CAAutoArrayDelete& operator=(U* ptr) 
	{
		set(ptr); 
		return *this;
	}
		
	T& operator*() const { return *ptr_; }
	T* operator->() const { return ptr_; }
	
	T* operator()() const { return ptr_; }
	T* get() const { return ptr_; }
	operator T*() const { return ptr_; }

	bool operator==(CAAutoArrayDelete const& that) const { return ptr_ == that.ptr_; }
	bool operator!=(CAAutoArrayDelete const& that) const { return ptr_ != that.ptr_; }
	bool operator==(T* ptr) const { return ptr_ == ptr; }
	bool operator!=(T* ptr) const { return ptr_ != ptr; }
		
	T* release() 
	{
		// release ownership
		T* result = ptr_;
		ptr_ = 0;
		return result;
	}
	
	void set(T* ptr)
	{
		if (ptr != ptr_)
		{
			delete [] ptr_;
			ptr_ = ptr;
		}
	}
	
	void free() 
	{
		set(0);
	}


	// automatic conversions to allow assignment from results of functions.
	// hard to explain. see auto_ptr implementation and/or Josuttis' STL book.
	CAAutoArrayDelete(CAPtrRef<T> ref) : ptr_(ref.ptr_) { }

	CAAutoArrayDelete& operator=(CAPtrRef<T> ref)
	{
		set(ref.ptr_);
		return *this;
	}

	template<typename U>
	operator CAPtrRef<U>()
		{ return CAPtrRef<U>(release()); }

	template<typename U>
	operator CAAutoArrayDelete<U>()
		{ return CAAutoFree<U>(release()); }
	
};





// convenience function
template <typename T>
void free(CAAutoFree<T>& p)
{
	p.free();
}

////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////

#if 0
// example program showing ownership transfer

CAAutoFree<char> source()
{
	// source allocates and returns ownership to the caller.
	const char* str = "this is a test";
	size_t size = strlen(str) + 1;
	CAAutoFree<char> captr(size, false);
	strlcpy(captr(), str, size);
	printf("source %08X %08X '%s'\n", &captr, captr(), captr());
	return captr;
}

void user(CAAutoFree<char> const& captr)
{
	// passed by const reference. user can access the pointer but does not take ownership.
	printf("user: %08X %08X '%s'\n", &captr, captr(), captr());
}

void sink(CAAutoFree<char> captr)
{
	// passed by value. sink takes ownership and frees the pointer on return.
	printf("sink: %08X %08X '%s'\n", &captr, captr(), captr());
}


int main (int argc, char * const argv[]) 
{

	CAAutoFree<char> captr(source());
	printf("main captr A %08X %08X\n", &captr, captr());
	user(captr);
	sink(captr);
	printf("main captr B %08X %08X\n", &captr, captr());
    return 0;
}
#endif

#endif
