//+-------------------------------------------------------------------------
//
//  Microsoft Windows
//
//  Copyright (C) Microsoft Corporation, 1999 - 1999
//
//  File:       autoptr.h
//
//--------------------------------------------------------------------------

#ifndef AUTOPTR_H_INCLUDED
#define AUTOPTR_H_INCLUDED

#ifndef ASSERT
#ifndef _INC_CRTDBG
#include <crtdbg.h>
#endif // _INC_CRTDBG
#define ASSERT(x) _ASSERT(x)
#endif // ASSERT

#include "cpputil.h"

/*+-------------------------------------------------------------------------*
 * CAutoResourceManagementBase
 *
 * This is a base class that implements common functionality for the class
 * of smart resource handlers which release resource when it's destroyed.  All
 * classes based on this class will behave identically, except the manner
 * in which they release their resources.
 *
 * DeleterClass is typically the class that derives from CAutoResourceManagementBase,
 * and must implement
 *
 *      static void _Delete(ResourceType h);
 *
 * See CAutoPtr below for an example.
 *--------------------------------------------------------------------------*/

template<typename ResourceType, typename DeleterClass>
class CAutoResourceManagementBase
{
    typedef CAutoResourceManagementBase<ResourceType, DeleterClass> ThisClass;
    typedef ThisClass Releaser;

    DECLARE_NOT_COPIABLE   (ThisClass)
    DECLARE_NOT_ASSIGNABLE (ThisClass)

// protected ctor so only derived classes can intantiate
protected:
    explicit CAutoResourceManagementBase(ResourceType h = 0) throw() : m_hResource(h) {}

public:
    ~CAutoResourceManagementBase() throw()
    {
        Delete();
    }

    void Attach(ResourceType p) throw()
    {
        ASSERT(m_hResource == NULL);
        m_hResource = p;
    }

    ResourceType Detach() throw()
    {
        ResourceType const p = m_hResource;
        m_hResource = NULL;
        return p;
    }

    /*
     * Returns the address of the pointer contained in this class.
     * This is useful when using the COM/OLE interfaces to create
     * allocate the object that this class manages.
     */
    ResourceType* operator&() throw()
    {
        /*
         * This object must be empty now, or the data pointed to will be leaked.
         */
        ASSERT (m_hResource == NULL);
        return &m_hResource;
    }

    operator ResourceType() const throw()
    {
        return m_hResource;
    }

    bool operator==(int p) const throw()
    {
        ASSERT(p == NULL);
        return m_hResource == NULL;
    }

    bool operator!=(int p) const throw()
    {
        ASSERT(p == NULL);
        return m_hResource != NULL;
    }

    bool operator!() const throw()
    {
        return m_hResource == NULL;
    }

    void Delete() throw()
    {
        if (m_hResource != NULL)
        {
            DeleterClass::_Delete (m_hResource);
            m_hResource = NULL;
        }
    }

private:
    ResourceType m_hResource;
}; // class CAutoResourceManagementBase


/*+-------------------------------------------------------------------------*
 * CAutoPtrBase
 *
 * This is a base class that implements common functionality for the class
 * of smart pointers which delete its pointee when it's destroyed.  All
 * classes based on this class will behave identically, except the manner
 * in which they destroy their pointees.
 *
 * DeleterClass is typically the class that derives from CAutoPtrBase, and
 * must implement
 *
 *      static void _Delete(T* p);
 *
 * This template reuses CAutoResourceManagementBase to manage the pointer
 *
 * See CAutoPtr below for an example.
 *--------------------------------------------------------------------------*/

template<typename T, typename DeleterClass>
class CAutoPtrBase : public CAutoResourceManagementBase<T*, DeleterClass>
{
    typedef CAutoPtrBase<T, DeleterClass>                 ThisClass;
    typedef CAutoResourceManagementBase<T*, DeleterClass> BaseClass;
    typedef BaseClass Releaser;

    DECLARE_NOT_COPIABLE   (ThisClass)
    DECLARE_NOT_ASSIGNABLE (ThisClass)

// protected ctor so only derived classes can intantiate
protected:
    explicit CAutoPtrBase(T* p = 0) throw() : BaseClass(p) {}

public:

    T& operator*() const throw()
    {
        T* ptr = *this; // use operator defined by the BaseClass for conversion
        ASSERT(ptr != NULL);
        return *ptr;
    }

    T* operator->() const throw()
    {
        T* ptr = *this; // use operator defined by the BaseClass for conversion
        ASSERT(ptr != NULL);
        return ptr;
    }

}; // class CAutoPtrBase


/*+-------------------------------------------------------------------------*
 * CAutoPtr
 *
 * CAutoPtrBase-based class that deletes pointers allocated with
 * operator new.
 *--------------------------------------------------------------------------*/

template<class T>
class CAutoPtr : public CAutoPtrBase<T, CAutoPtr<T> >
{
    typedef CAutoPtrBase<T, CAutoPtr<T> > BaseClass;
    friend BaseClass::Releaser;

public:
    explicit CAutoPtr(T* p = 0) throw() : BaseClass(p)
    {}

private:
    // only CAutoPtrBase should call this
    static void _Delete (T* p)
    {
        delete p;
    }
};


/*+-------------------------------------------------------------------------*
 * CAutoArrayPtr
 *
 * CAutoPtrBase-based class that deletes pointers allocated with
 * operator new[].
 *--------------------------------------------------------------------------*/

template<class T>
class CAutoArrayPtr : public CAutoPtrBase<T, CAutoArrayPtr<T> >
{
    typedef CAutoPtrBase<T, CAutoArrayPtr<T> > BaseClass;
    friend BaseClass::Releaser;

public:
    explicit CAutoArrayPtr(T* p = 0) throw() : BaseClass(p)
    {}

private:
    // only CAutoPtrBase should call this
    static void _Delete (T* p)
    {
        delete[] p;
    }
};


/*+-------------------------------------------------------------------------*
 * CCoTaskMemPtr
 *
 * CAutoPtrBase-based class that deletes pointers allocated with
 * CoTaskMemAlloc.
 *--------------------------------------------------------------------------*/

template<class T>
class CCoTaskMemPtr : public CAutoPtrBase<T, CCoTaskMemPtr<T> >
{
    typedef CAutoPtrBase<T, CCoTaskMemPtr<T> > BaseClass;
    friend BaseClass::Releaser;

public:
    explicit CCoTaskMemPtr(T* p = 0) throw() : BaseClass(p)
    {}

private:
    // only CAutoPtrBase should call this
    static void _Delete (T* p)
    {
        if (p != NULL)
            CoTaskMemFree (p);
    }
};


/*+-------------------------------------------------------------------------*
 * CAutoGlobalPtr
 *
 * CAutoPtrBase-based class that deletes pointers allocated with GlobalAlloc.
 *--------------------------------------------------------------------------*/

template<class T>
class CAutoGlobalPtr : public CAutoPtrBase<T, CAutoGlobalPtr<T> >
{
    typedef CAutoPtrBase<T, CAutoGlobalPtr<T> > BaseClass;
    friend BaseClass::Releaser;

public:
    explicit CAutoGlobalPtr(T* p = 0) throw() : BaseClass(p)
    {}

private:
    // only CAutoPtrBase should call this
    static void _Delete (T* p)
    {
        if (p != NULL)
            GlobalFree (p);
    }
};


/*+-------------------------------------------------------------------------*
 * CAutoLocalPtr
 *
 * CAutoPtrBase-based class that deletes pointers allocated with LocalAlloc.
 *--------------------------------------------------------------------------*/

template<class T>
class CAutoLocalPtr : public CAutoPtrBase<T, CAutoLocalPtr<T> >
{
    typedef CAutoPtrBase<T, CAutoLocalPtr<T> > BaseClass;
    friend BaseClass::Releaser;

public:
    explicit CAutoLocalPtr(T* p = 0) throw() : BaseClass(p)
    {}

private:
    // only CAutoPtrBase should call this
    static void _Delete (T* p)
    {
        if (p != NULL)
            LocalFree (p);
    }
};


/*+-------------------------------------------------------------------------*
 * CHeapAllocMemPtr
 *
 * CAutoPtrBase-based class that deletes pointers allocated from the process
 * default heap with HeapAlloc.
 *--------------------------------------------------------------------------*/

template<class T>
class CHeapAllocMemPtr : public CAutoPtrBase<T, CHeapAllocMemPtr<T> >
{
    typedef CAutoPtrBase<T, CHeapAllocMemPtr<T> > BaseClass;
    friend BaseClass::Releaser;

public:
    explicit CHeapAllocMemPtr(T* p = 0) throw() : BaseClass(p)
    {}

private:
    // only CAutoPtrBase should call this
    static void _Delete (T* p)
    {
        if (p != NULL)
            HeapFree(::GetProcessHeap(), 0, p);
    }
};


/*+-------------------------------------------------------------------------*
 * CAutoWin32Handle
 *
 * CAutoPtrBase-based class that closes HANDLE on destruction
 *--------------------------------------------------------------------------*/
class CAutoWin32Handle : public CAutoResourceManagementBase<HANDLE, CAutoWin32Handle>
{
    typedef CAutoResourceManagementBase<HANDLE, CAutoWin32Handle> BaseClass;
    friend BaseClass::Releaser;

public:
    explicit CAutoWin32Handle(HANDLE p = NULL) throw() : BaseClass(p) {}

    bool IsValid()
    {
        return IsValid(*this); // use base class operator to convet to HANDLE
    }
private:
    static bool IsValid (HANDLE p)
    {
        return (p != NULL && p != INVALID_HANDLE_VALUE);
    }
    // only CAutoResourceManagementBase should call this
    static void _Delete (HANDLE p)
    {
        if (IsValid(p))
            CloseHandle(p);
    }
};

/*+-------------------------------------------------------------------------*
 * CAutoAssignOnExit
 *
 * instances of this template class assign the value in destructor.
 *
 * USAGE: Say you have variable "int g_status" which must be set to S_OK before
 *        you leave the function. To do so declare following in the function:
 *
 *        CAutoAssignOnExit<int,S_OK>  any_object_name(g_status);
 *--------------------------------------------------------------------------*/
template<typename T, T value>
class CAutoAssignOnExit
{
    T& m_rVariable; // variable, which needs to be modified in destructor
public:
    // constructor
    CAutoAssignOnExit( T& rVariable ) : m_rVariable(rVariable) {}
    // destructor
    ~CAutoAssignOnExit()
    {
        // assign designated final value
        m_rVariable = value;
    }
};

#endif // AUTOPTR_H_INCLUDED