#ifndef CORELIB___NCBIOBJ__HPP
#define CORELIB___NCBIOBJ__HPP

/*  $Id: ncbiobj.hpp 667211 2023-05-10 17:44:44Z vasilche $
 * ===========================================================================
 *
 *                            PUBLIC DOMAIN NOTICE
 *               National Center for Biotechnology Information
 *
 *  This software/database is a "United States Government Work" under the
 *  terms of the United States Copyright Act.  It was written as part of
 *  the author's official duties as a United States Government employee and
 *  thus cannot be copyrighted.  This software/database is freely available
 *  to the public for use. The National Library of Medicine and the U.S.
 *  Government have not placed any restriction on its use or reproduction.
 *
 *  Although all reasonable efforts have been taken to ensure the accuracy
 *  and reliability of the software and data, the NLM and the U.S.
 *  Government do not and cannot warrant the performance or results that
 *  may be obtained by using this software or data. The NLM and the U.S.
 *  Government disclaim all warranties, express or implied, including
 *  warranties of performance, merchantability or fitness for any particular
 *  purpose.
 *
 *  Please cite the author in any work or product based on this material.
 *
 * ===========================================================================
 *
 * Author:  Eugene Vasilchenko, Pavel Ivanov
 *
 *
 */

/// @file ncbiobj.hpp
/// Portable reference counted smart and weak pointers using
/// CWeakRef, CRef, CObject and CObjectEx.


#include <corelib/ncbicntr.hpp>
#include <corelib/ncbiatomic.hpp>
#include <corelib/ddumpable.hpp>

/// this relieves us of some nastiness with Win32's version of GetObject(),
/// which prevents us from using CRef<>::GetObject in lots of places
#if defined(NCBI_OS_MSWIN)
#  include <corelib/ncbi_os_mswin.hpp>
#endif


/** @addtogroup Object
 *
 * @{
 */



BEGIN_NCBI_SCOPE

class CObjectMemoryPool;
class CObject;
class CObjectEx;


/////////////////////////////////////////////////////////////////////////////
///
/// CObjectException --
///
/// Define exceptions generated by CObject.
///
/// CObjectException inherits its basic functionality from CCoreException
/// and defines additional error codes.

class NCBI_XNCBI_EXPORT CObjectException : public CCoreException
{
public:
    /// Error types that an application can generate.
    enum EErrCode {
        eRefDelete,     ///< Attempt to delete valid reference
        eDeleted,       ///< Attempt to delete a deleted object
        eCorrupted,     ///< Object corrupted error
        eRefOverflow,   ///< Reference overflow error
        eNoRef,         ///< Attempt to access an object that is unreferenced
        eRefUnref,      ///< Attempt to make a referenced object an
                        ///< unreferenced one
        eHeapState      ///< Attempt to make incorrect in-heap state
    };

    /// Translate from the error code value to its string representation.
    virtual const char* GetErrCodeString(void) const override;

    // Standard exception boilerplate code.
    NCBI_EXCEPTION_DEFAULT(CObjectException, CCoreException);

protected:
    void x_InitErrCode(CException::EErrCode err_code) override;
};


////////////////////////////////////////////////////////////////////////////
/// CObjectCounterLocker --
///     Default locker class for CRef/CConstRef templates,
///     all other locker classes should be subclasses of CObjectCounterLocker
///     and use its locking methods.
/// The CObjectCounterLocker in Debug mode allows to monitor locking/unlocking
/// of some object class. The locking methods are non-inlined for this purpose.
/// Monitored class is controlled by static methods MonitorObjectType() and
/// StopMonitoring(). Currently locked objects and CRef<> pointers to them
/// can be printed by calling static method ReportLockedObjects().
////////////////////////////////////////////////////////////////////////////

#ifdef _DEBUG
# define NCBI_OBJECT_LOCKER_EXPORT NCBI_XNCBI_EXPORT
#else
# define NCBI_OBJECT_LOCKER_EXPORT
# define NCBI_OBJECT_LOCKER_INLINE
#endif

class CObjectCounterLocker
{
public:
    // Mark object as "locked" from deletion.
    void NCBI_OBJECT_LOCKER_EXPORT Lock(const CObject* object) const;

    // Mark object as "locked" from deletion if it was already locked by
    // another locker object.
    // Preconditions: this locker was assigned from the another locker object.
    void NCBI_OBJECT_LOCKER_EXPORT Relock(const CObject* object) const;

    // Mark object as "unlocked" for deletion,
    // delete it if last lock was removed.
    void NCBI_OBJECT_LOCKER_EXPORT Unlock(const CObject* object) const;

    // Mark object as "unlocked" for deletion, but do not delete it.
    void NCBI_OBJECT_LOCKER_EXPORT UnlockRelease(const CObject* object) const;

    // Transfer lock from other locker
    void NCBI_OBJECT_LOCKER_EXPORT TransferLock(const CObject* object,
                                                const CObjectCounterLocker& old_locker) const;

    NCBI_NORETURN static
    void NCBI_XNCBI_EXPORT ReportIncompatibleType(const type_info& type);

    /// Set monitored object type, e.g. typeid(CScope)
    /// The method has no effect in Release mode.
    static
    void NCBI_XNCBI_EXPORT MonitorObjectType(const type_info& type);
    /// Stop lock/unlock monitoring.
    /// The method has no effect in Release mode.
    static
    void NCBI_XNCBI_EXPORT StopMonitoring(void);
    /// Print all currently locked objects of monitored type.
    /// The method has no effect in Release mode.
    static
    void NCBI_XNCBI_EXPORT ReportLockedObjects(bool clear = false);
};


/////////////////////////////////////////////////////////////////////////////
// Traits for default locker parameter
/////////////////////////////////////////////////////////////////////////////

template <class C>
class CLockerTraits
{
public:
    typedef CObjectCounterLocker TLockerType;
};


/////////////////////////////////////////////////////////////////////////////
///
/// CObject --
///
/// Define the CObject which stores the reference count and the object.
///
/// CObject inherits from CDebugDumpable the ability to "dump" diagnostic
/// information useful for debugging.

class CObject : public CDebugDumpable
{
public:
    /// Default locker type for CRef
    typedef CObjectCounterLocker TLockerType;
    /// Constructor.
    NCBI_XNCBI_EXPORT
    CObject(void);

    /// Copy constructor.
    NCBI_XNCBI_EXPORT
    CObject(const CObject& src);

    /// Destructor.
    NCBI_XNCBI_EXPORT
    virtual ~CObject(void);

    /// Assignment operator.
    CObject& operator=(const CObject& src) THROWS_NONE;

    /// Check if object can be deleted.
    bool CanBeDeleted(void) const THROWS_NONE;

    /// Check if object is allocated in memory pool (not system heap)
    bool IsAllocatedInPool(void) const THROWS_NONE;

    /// Check if object is referenced.
    bool Referenced(void) const THROWS_NONE;

    /// Check if object is referenced only once.
    bool ReferencedOnlyOnce(void) const THROWS_NONE;

    /// Add reference to object.
    void AddReference(void) const;

    /// Remove reference to object.
    void RemoveReference(void) const;

    /// Remove reference without deleting object.
    NCBI_XNCBI_EXPORT
    void ReleaseReference(void) const;

    /// Mark this object as not allocated in heap --  do not delete this
    /// object.
    NCBI_XNCBI_EXPORT
    virtual void DoNotDeleteThisObject(void);

    /// Mark this object as allocated in heap -- object can be deleted.
    NCBI_XNCBI_EXPORT
    virtual void DoDeleteThisObject(void);

    // operators new/delete for additional checking in debug mode

    /// Define new operator for memory allocation.
    NCBI_XNCBI_EXPORT
    void* operator new(size_t size);

    /// Define new[] operator for 'array' memory allocation.
    NCBI_XNCBI_EXPORT
    void* operator new[](size_t size);

    /// Define delete operator for memory deallocation.
    NCBI_XNCBI_EXPORT
    void operator delete(void* ptr);

    /// Define delete[] operator for memory deallocation.
    NCBI_XNCBI_EXPORT
    void operator delete[](void* ptr);

    /// Define new operator.
    NCBI_XNCBI_EXPORT
    void* operator new(size_t size, void* place);

    /// Define delete operator.
    NCBI_XNCBI_EXPORT
    void operator delete(void* ptr, void* place);

    /// Define new operator using memory pool.
    NCBI_XNCBI_EXPORT
    void* operator new(size_t size, CObjectMemoryPool* place);

    /// Define delete operator.
    NCBI_XNCBI_EXPORT
    void operator delete(void* ptr, CObjectMemoryPool* place);

    /// Define method for dumping debug information.
    NCBI_XNCBI_EXPORT
    virtual void DebugDump(CDebugDumpContext ddc, unsigned int depth) const;

    /// Define method to throw null pointer exception.
    ///
    /// Static method through which all CRef<> / CConstRef<> null pointer
    /// throws travel.  This is done to avoid an inline throw.
    NCBI_NORETURN NCBI_XNCBI_EXPORT
    static void ThrowNullPointerException(void);
    NCBI_NORETURN NCBI_XNCBI_EXPORT
    static void ThrowNullPointerException(const type_info& type);


    /// Control filling of newly allocated memory
    /// 
    /// Default mode is eAllocFillNone if not changed by configuration.
    ///   eAllocFillNone - do not fill at all
    ///       this mode is faster and is necessary to avoid interference with
    ///       memory checker programs like Valgrind.
    ///   eAllocFillZero - fill new memory with zeros (old default mode)
    ///   eAllocFillPattern - fill with non-zero pattern
    enum EAllocFillMode {
        eAllocFillNone = 1,
        eAllocFillZero,
        eAllocFillPattern
    };
    static EAllocFillMode GetAllocFillMode(void);
    static void SetAllocFillMode(EAllocFillMode mode);
    /// Set mode from configuration parameter value.
    static void SetAllocFillMode(const string& value);

protected:
    /// Virtual method "deleting" this object.
    /// Method is called whenever by all other indicators this object should
    /// be deleted. These indicators are: last reference to the object is
    /// removed, object created on heap and method DoNotDeleteThisObject()
    /// was not called. Default implementation actually deletes the object,
    /// but derived classes are free to do whatever they want (e.g. if they
    /// know that they are allocated at some pool they should return
    /// themselves to this pool).
    NCBI_XNCBI_EXPORT
    virtual void DeleteThis(void);

public:
    typedef atomic<Uint8>    TCounter;  ///< Counter type is CAtomiCounter
    typedef Uint8            TCount;    ///< Alias for value type of counter

    /// Define possible object states.
    /// 
    /// When TCounter is signed, all valid values start with 01;
    /// when it is unsigned, they start with 1. In other words, when
    /// TCounter is signed, the msb (most significant bit) is 0, and the bit
    /// after that is a 1; and if the counter is unsigned, then the msb is 1.
    ///
    /// Least significant bits are the "memory" bits and the most
    /// significant bit (or the one after it) is the "valid" bit.
    ///
    /// The following bit positions have special significance:
    /// - Least significant bit = 0 means object not in heap.
    /// - Least significant bit = 1 means object in heap.
    /// - Most significant bit (or one after it) = 0 means object not valid
    /// - Most significant bit (or one after it) = 1 means object is valid 
    ///
    /// Possible bit patterns:
    /// - [0]0x...xxxx : non valid object -> cannot be referenced.
    /// - [0]1c...ccx0 : object not in heap -> cannot be deleted.
    /// - [0]1c...cc01 : object in heap pool -> can be deleted via CMemoryPool
    /// - [0]1c...cc11 : object in heap -> can be deleted.

    /// Detected as in heap
    static const TCount eCounterBitsCanBeDeleted = 1 << 0;
    /// Heap signature was found
    static const TCount eCounterBitsInPlainHeap = 1 << 1;
    /// Mask for 'in heap' state flags
    static const TCount eCounterBitsPlaceMask =
        eCounterBitsCanBeDeleted | eCounterBitsInPlainHeap;
    /// Skip over the "in heap" bits
    static const int eCounterStep      = 1 << 2;

    /// Minimal value for valid objects (reference counter is zero)
    /// Must be a single bit value.
    /// All counter values less than this value are invalid.
#ifdef NCBI_COUNTER_UNSIGNED
    /// 1 in the left most of the valid bits -- unsigned case
    static const TCount eCounterValid   = TCount(1) << (sizeof(TCount) * 8 - 1);
#else
    /// 1 in the left most of the valid bits -- signed case
    static const TCount eCounterValid   = TCount(1) << (sizeof(TCount) * 8 - 2);
#endif
    /// Valid object, and object in heap. 
    static const TCount eCounterStateMask =
        eCounterValid | eCounterBitsPlaceMask;

private:
    friend class CObjectMemoryPool;
    friend class CWeakObject;

    // special methods for parsing object state number

    /// Check if object state is valid.
    static bool ObjectStateValid(TCount count);

    /// Check if object can be deleted.
    static bool ObjectStateCanBeDeleted(TCount count);

    /// Check if object is allocated in memory pool.
    static bool ObjectStateIsAllocatedInPool(TCount count);

    /// Check if object can be referenced.
    static bool ObjectStateUnreferenced(TCount count);

    /// Check if object can be referenced.
    static bool ObjectStateReferenced(TCount count);

    /// Check if object can be referenced only once.
    static bool ObjectStateReferencedOnlyOnce(TCount count);

    /// Initialize counter. 
    void InitCounter(void);

    /// Remove the last reference.
    NCBI_XNCBI_EXPORT
    void RemoveLastReference(TCount count) const;

    // report different kinds of error

    /// Report object is invalid.
    ///
    /// Example: Attempt to use a deleted object.
    void InvalidObject(void) const;

    /// Report that counter has overflowed.
    NCBI_NORETURN NCBI_XNCBI_EXPORT
    void CheckReferenceOverflow(TCount count) const;

    mutable TCounter  m_Counter;  ///< The actual reference counter
};


////////////////////////////////////////////////////////////////////////////
// Inline methods of CObject
////////////////////////////////////////////////////////////////////////////


inline
bool CObject::ObjectStateCanBeDeleted(TCount count)
{
    // check only 'CanBeDeleted' bit, include both plain heap and memory pool
    return (count & eCounterBitsCanBeDeleted) != 0;
}


inline
bool CObject::ObjectStateIsAllocatedInPool(TCount count)
{
    // check if 'CanBeDeleted' is set and InPlainHeap is not set
    return (count & eCounterBitsPlaceMask) == eCounterBitsCanBeDeleted;
}


inline
bool CObject::ObjectStateValid(TCount count)
{
    return count >= eCounterValid;
}


inline
bool CObject::ObjectStateReferenced(TCount count)
{
    return count >= eCounterValid + eCounterStep;
}


inline
bool CObject::ObjectStateUnreferenced(TCount count)
{
    return (count & ~eCounterBitsPlaceMask) == eCounterValid;
}


inline
bool CObject::ObjectStateReferencedOnlyOnce(TCount count)
{
    return (count & ~eCounterBitsPlaceMask) == eCounterValid + eCounterStep;
}


inline
bool CObject::CanBeDeleted(void) const THROWS_NONE
{
    return ObjectStateCanBeDeleted(m_Counter);
}


inline
bool CObject::IsAllocatedInPool(void) const THROWS_NONE
{
    return ObjectStateIsAllocatedInPool(m_Counter);
}


inline
bool CObject::Referenced(void) const THROWS_NONE
{
    return ObjectStateReferenced(m_Counter);
}


inline
bool CObject::ReferencedOnlyOnce(void) const THROWS_NONE
{
    return ObjectStateReferencedOnlyOnce(m_Counter);
}


inline
CObject& CObject::operator=(const CObject& ) THROWS_NONE
{
    return *this;
}


inline
void CObject::AddReference(void) const
{
    TCount newCount = (m_Counter += eCounterStep);
    if ( !ObjectStateReferenced(newCount) ) {
        m_Counter -= eCounterStep; // undo
        CheckReferenceOverflow(newCount - eCounterStep);
    }
}


inline
void CObject::RemoveReference(void) const
{
    TCount newCount = (m_Counter -= eCounterStep);
    if ( !ObjectStateReferenced(newCount) ) {
        RemoveLastReference(newCount);
    }
}


////////////////////////////////////////////////////////////////////////////
// CObjectCounterLocker inline methods
////////////////////////////////////////////////////////////////////////////

#ifdef NCBI_OBJECT_LOCKER_INLINE
// debug version of CObjectCounterLocker allows monitoring some object type
inline
void CObjectCounterLocker::Lock(const CObject* object) const
{
    object->AddReference();
}


inline
void CObjectCounterLocker::Relock(const CObject* object) const
{
    Lock(object);
}


inline
void CObjectCounterLocker::Unlock(const CObject* object) const
{
    object->RemoveReference();
}


inline
void CObjectCounterLocker::UnlockRelease(const CObject* object) const
{
    object->ReleaseReference();
}


inline
void CObjectCounterLocker::TransferLock(const CObject* /*object*/,
                                        const CObjectCounterLocker& /*old_locker*/) const
{
}
#endif


////////////////////////////////////////////////////////////////////////////
// Locker class for interfaces, later derived from CObject
////////////////////////////////////////////////////////////////////////////

template<class Interface>
class CInterfaceObjectLocker : public CObjectCounterLocker
{
public:
    void Lock(const Interface* object) const
        {
            const CObject* cobject = dynamic_cast<const CObject*>(object);
            if ( !cobject ) {
                CObjectCounterLocker::ReportIncompatibleType(typeid(*object));
            }
            CObjectCounterLocker::Lock(cobject);
        }

    void Relock(const Interface* object) const
        {
            const CObject* cobject = dynamic_cast<const CObject*>(object);
            _ASSERT(cobject);
            CObjectCounterLocker::Relock(cobject);
        }

    void Unlock(const Interface* object) const
        {
            const CObject* cobject = dynamic_cast<const CObject*>(object);
            _ASSERT(cobject);
            CObjectCounterLocker::Unlock(cobject);
        }

    void UnlockRelease(const Interface* object) const
        {
            const CObject* cobject = dynamic_cast<const CObject*>(object);
            _ASSERT(cobject);
            CObjectCounterLocker::UnlockRelease(cobject);
        }

#ifdef NCBI_OBJECT_LOCKER_INLINE
    void TransferLock(const Interface* /*object*/,
                      const CInterfaceObjectLocker<Interface>& /*old_locker*/) const
        {
        }
#else
    // only non-inline method does something
    void TransferLock(const Interface* object,
                      const CInterfaceObjectLocker<Interface>& old_locker) const
        {
            const CObject* cobject = dynamic_cast<const CObject*>(object);
            _ASSERT(cobject);
            CObjectCounterLocker::TransferLock(cobject, old_locker);
        }
#endif
};


/////////////////////////////////////////////////////////////////////////////
///
/// CRef --
///
/// Define a template class that stores a pointer to an object and defines
/// methods for referencing that object.

template<class C, class Locker = typename CLockerTraits<C>::TLockerType> class CRef;
template<class C, class Locker = typename CLockerTraits<C>::TLockerType> class CConstRef;

template<class C, class Locker>
class CRef {
public:
    typedef C element_type;             ///< Define alias element_type
    typedef element_type TObjectType;   ///< Define alias TObjectType
    typedef Locker locker_type;         ///< Define alias for locking type
    typedef CRef<C, Locker> TThisType;  ///< Alias for this template type

    /// Helper template to template enable methods only for derived types
    template<class T>
    using enable_if_derived =
        typename std::enable_if<std::is_convertible<T*, TObjectType*>::value>;
    template<class TDerived, class TLocker> friend class CRef;
    template<class TDerived, class TLocker> friend class CConstRef;
    
    /// Constructor for null pointer.
    inline
    CRef(void) THROWS_NONE
        {
        }

    /// Constructor for ENull pointer.
    inline
    CRef(ENull /*null*/) THROWS_NONE
        {
        }

    /// Constructor for explicit type conversion from pointer to object.
    explicit CRef(TObjectType* ptr)
        : m_Data(locker_type(), ptr)
        {
            x_LockFromPtr();
        }

    /// Constructor for explicit type conversion from pointer to object.
    CRef(TObjectType* ptr, const locker_type& locker_value)
        : m_Data(locker_value, ptr)
        {
            x_LockFromPtr();
        }

    /// Copy constructor from an existing CRef object
    CRef(const TThisType& ref)
        : m_Data(ref.m_Data)
        {
            x_LockFromRef();
        }

    /// Copy constructor from an existing CRef object of derived type
    template<class TDerived,
             class = typename enable_if_derived<TDerived>::type>
    CRef(const CRef<TDerived, Locker>& ref)
        : m_Data(ref.GetLocker(), ref.GetNCPointerOrNull())
        {
            x_LockFromRef();
        }

    /// Move constructor from an existing CRef object
    CRef(TThisType&& ref)
        : m_Data(ref.m_Data)
        {
            x_LockFromMoveConstructor(ref.m_Data.first());
            ref.m_Data.second() = 0;
        }

    /// Move constructor from an existing CRef object of derived type
    template<class TDerived,
             class = typename enable_if_derived<TDerived>::type>
    CRef(CRef<TDerived, Locker>&& ref)
        : m_Data(ref.GetLocker(), ref.GetNCPointerOrNull())
        {
            x_LockFromMoveConstructor(ref.m_Data.first());
            ref.m_Data.second() = 0;
        }

    /// Destructor.
    ~CRef(void)
        {
            Reset();
        }

    /// Get reference to locker object
    const locker_type& GetLocker(void) const
        {
            return m_Data.first();
        }

    /// Check if CRef is empty -- not pointing to any object, which means
    /// having a null value. 
    ///
    /// @sa
    ///   Empty(), IsNull()
    bool operator!(void) const THROWS_NONE
        {
            return m_Data.second() == 0;
        }

    /// Check if CRef is empty -- not pointing to any object, which means
    /// having a null value. 
    ///
    /// @sa
    ///   IsNull(), operator!()
    bool Empty(void) const THROWS_NONE
        {
            return m_Data.second() == 0;
        }

    /// Check if CRef is not empty -- pointing to an object and has
    /// a non-null value. 
    bool NotEmpty(void) const THROWS_NONE
        {
            return m_Data.second() != 0;
        }

    /// Check if pointer is null -- same effect as Empty().
    ///
    /// @sa
    ///   Empty(), operator!()
    bool IsNull(void) const THROWS_NONE
        {
            return m_Data.second() == 0;
        }

    /// Check if pointer is not null -- same effect as NotEmpty().
    ///
    /// @sa
    ///   NotEmpty()
    bool NotNull(void) const THROWS_NONE
        {
            return m_Data.second() != 0;
        }

    /// Swaps the pointer with another reference
    ///
    /// @sa
    ///   Swap(CRef<>&)
    inline
    void Swap(TThisType& ref)
        {
            swap(m_Data, ref.m_Data);
            if ( TObjectType* ptr = m_Data.second() ) {
                m_Data.first().TransferLock(ptr, ref.m_Data.first());
            }
            if ( TObjectType* ptr = ref.m_Data.second() ) {
                ref.m_Data.first().TransferLock(ptr, m_Data.first());
            }
        }

    /// Reset reference object.
    ///
    /// This sets the pointer to object to null, and removes reference
    /// count to object and deletes the object if this is the last reference
    /// to the object.
    /// @sa
    ///   Reset(TObjectType*)
    inline
    void Reset(void)
        {
            TObjectType* ptr = m_Data.second();
            if ( ptr ) {
                m_Data.second() = 0;
                m_Data.first().Unlock(ptr);
            }
        }

    /// Reset reference object to new pointer.
    ///
    /// This sets the pointer to object to the new pointer, and removes
    /// reference count to old object and deletes the old object if this is
    /// the last reference to the old object.
    /// @sa
    ///   Reset()
    inline
    void Reset(TObjectType* newPtr)
        {
            TObjectType* oldPtr = m_Data.second();
            if ( newPtr != oldPtr ) {
                if ( newPtr ) {
                    m_Data.first().Lock(newPtr);
                }
                m_Data.second() = newPtr;
                if ( oldPtr ) {
                    m_Data.first().Unlock(oldPtr);
                }
            }
        }

    /// Release a reference to the object and return a pointer to the object.
    ///
    /// Releasing a reference means decreasing the reference count by "1". A
    /// pointer to the existing object is returned, unless this pointer is
    /// already null(0), in which case a null(0) is returned.
    ///
    /// Similar to Release(), except that this method returns a null,
    /// whereas Release() throws a null pointer exception.
    ///
    /// @sa
    ///   Release()
    inline
    TObjectType* ReleaseOrNull(void)
        {
            TObjectType* ptr = m_Data.second();
            if ( !ptr ) {
                return 0;
            }
            m_Data.second() = 0;
            m_Data.first().UnlockRelease(ptr);
            return ptr;
        }

    NCBI_NORETURN
    static void ThrowNullPointerException(void)
        {
            CObject::ThrowNullPointerException(/*typeid(TObjectType*)*/);
        }

    /// Release a reference to the object and return a pointer to the object.
    ///
    /// Releasing a reference means decreasing the reference count by "1". A
    /// pointer to the existing object is returned, unless this pointer is
    /// already null(0), in which the null pointer exception (eNullPtr) is
    /// thrown.
    ///
    /// Similar to ReleaseOrNull(), except that this method throws an exception
    /// whereas ReleaseOrNull() does not.
    ///
    /// @sa
    ///   ReleaseOrNull()
    inline
    TObjectType* Release(void)
        {
            TObjectType* ptr = m_Data.second();
            if ( !ptr ) {
                ThrowNullPointerException();
            }
            m_Data.second() = 0;
            m_Data.first().UnlockRelease(ptr);
            return ptr;
        }

    /// Reset reference object to new pointer.
    ///
    /// This sets the pointer to object to the new pointer, and removes
    /// reference count to old object and deletes the old object if this is
    /// the last reference to the old object.
    /// The new pointer is got from ref argument.
    /// Operation is atomic on this object, so that AtomicResetFrom() and
    /// AtomicReleaseTo() called from different threads will work properly.
    /// Operation is not atomic on ref argument.
    /// @sa
    ///   AtomicReleaseTo(CRef& ref);
    inline
    void AtomicResetFrom(const TThisType& ref)
        {
            TObjectType* ptr = ref.m_Data.second();
            if ( ptr )
                m_Data.first().Lock(ptr); // for this
            TObjectType* old_ptr = AtomicSwap(ptr);
            if ( old_ptr )
                m_Data.first().Unlock(old_ptr);
        }
    /// Release referenced object to another CRef<> object.
    ///
    /// This copies the pointer to object to the argument ref,
    /// and release reference from this object.
    /// Old reference object held by argument ref is released and deleted if
    /// necessary.
    /// Operation is atomic on this object, so that AtomicResetFrom() and
    /// AtomicReleaseTo() called from different threads will work properly.
    /// Operation is not atomic on ref argument.
    /// @sa
    ///   AtomicResetFrom(const CRef& ref);
    inline
    void AtomicReleaseTo(TThisType& ref)
        {
            TObjectType* old_ptr = AtomicSwap(0);
            if ( old_ptr ) {
                ref.Reset(old_ptr);
                m_Data.first().Unlock(old_ptr);
            }
            else {
                ref.Reset();
            }
        }

    /// Assignment operator for references.
    TThisType& operator=(const TThisType& ref)
        {
            x_AssignFromRef(ref.m_Data.second());
            return *this;
        }

    /// Assignment operator for references of derived types
    template<class TDerived,
             class = typename enable_if_derived<TDerived>::type>
    TThisType& operator=(const CRef<TDerived, Locker>& ref)
        {
            x_AssignFromRef(ref.m_Data.second());
            return *this;
        }

    /// Move assignment operator for references.
    TThisType& operator=(TThisType&& ref)
        {
#ifdef NCBI_COMPILER_MSVC
            // extra check on MSVC
            if (this == &ref) {
                // no-op
                return *this;
            }
#endif
            x_MoveAssign(ref.m_Data.first(), ref.m_Data.second());
            ref.m_Data.second() = 0;
            return *this;
        }

    /// Move assignment operator for references of derived types
    template<class TDerived,
             class = typename enable_if_derived<TDerived>::type>
    TThisType& operator=(CRef<TDerived, Locker>&& ref)
        {
            x_MoveAssign(ref.m_Data.first(), ref.m_Data.second());
            ref.m_Data.second() = 0;
            return *this;
        }

    /// Assignment operator for references with right hand side set to
    /// a pointer.
    TThisType& operator=(TObjectType* ptr)
        {
            Reset(ptr);
            return *this;
        }

    /// Assignment operator with right hand side set to ENull.
    TThisType& operator=(ENull /*null*/)
        {
            Reset();
            return *this;
        }

    /// Get pointer value and throw a null pointer exception if pointer
    /// is null.
    ///
    /// Similar to GetPointerOrNull() except that this method throws a null
    /// pointer exception if pointer is null, whereas GetPointerOrNull()
    /// returns a null value.
    ///
    /// @sa
    ///   GetPointerOrNull(), GetPointer(), GetObject()
    inline
    TObjectType* GetNonNullPointer(void)
        {
            TObjectType* ptr = m_Data.second();
            if ( !ptr ) {
                ThrowNullPointerException();
            }
            return ptr;
        }

    /// Get pointer value.
    ///
    /// Similar to GetNonNullPointer() except that this method returns a null
    /// if the pointer is null, whereas GetNonNullPointer() throws a null
    /// pointer exception.
    ///
    /// @sa
    ///   GetNonNullPointer()
    inline
    TObjectType* GetPointerOrNull(void) THROWS_NONE
        {
            return m_Data.second();
        }

    /// Get pointer,
    ///
    /// Same as GetPointerOrNull().
    ///
    /// @sa
    ///   GetPointerOrNull()
    inline
    TObjectType* GetPointer(void) THROWS_NONE
        {
            return GetPointerOrNull();
        }

    /// Get object.
    ///
    /// Similar to GetNonNullPointer(), except that this method returns the
    /// object whereas GetNonNullPointer() returns a pointer to the object.
    /// 
    /// @sa
    ///   GetNonNullPointer()
    inline
    TObjectType& GetObject(void)
        {
            return *GetNonNullPointer();
        }

    /// Dereference operator returning object.
    ///
    /// @sa
    ///   GetObject()
    inline
    TObjectType& operator*(void)
        {
            return *GetNonNullPointer();
        }

    /// Reference operator.
    ///
    /// @sa
    ///   GetPointer()
    inline
    TObjectType* operator->(void)
        {
            return GetNonNullPointer();
        }

    // Const getters.

    /// Get pointer value and throw a null pointer exception if pointer
    /// is null -- constant version.
    ///
    /// Similar to GetPointerOrNull() except that this method throws a null
    /// pointer exception if pointer is null, whereas GetPointerOrNull()
    /// returns a null value.
    ///
    /// @sa
    ///   GetPointerOrNull(), GetPointer(), GetObject()
    const TObjectType* GetNonNullPointer(void) const
        {
            const TObjectType* ptr = m_Data.second();
            if ( !ptr ) {
                ThrowNullPointerException();
            }
            return ptr;
        }

    /// Get pointer value -- constant version.
    ///
    /// Similar to GetNonNullPointer() except that this method returns a null
    /// if the pointer is null, whereas GetNonNullPointer() throws a null
    /// pointer exception.
    ///
    /// @sa
    ///   GetNonNullPointer()
    const TObjectType* GetPointerOrNull(void) const THROWS_NONE
        {
            return m_Data.second();
        }

    /// Get pointer -- constant version,
    ///
    /// Same as GetPointerOrNull().
    ///
    /// @sa
    ///   GetPointerOrNull()
    inline
    const TObjectType* GetPointer(void) const THROWS_NONE
        {
            return GetPointerOrNull();
        }

    /// Get object -- constant version.
    ///
    /// Similar to GetNonNullPointer(), except that this method returns the
    /// object whereas GetNonNullPointer() returns a pointer to the object.
    /// 
    /// @sa
    ///   GetNonNullPointer()
    inline
    const TObjectType& GetObject(void) const
        {
            return *GetNonNullPointer();
        }

    /// Dereference operator returning object -- constant version.
    ///
    /// @sa
    ///   GetObject()
    inline
    const TObjectType& operator*(void) const
        {
            return *GetNonNullPointer();
        }

    /// Reference operator -- constant version.
    ///
    /// @sa
    ///   GetPointer()
    inline
    const TObjectType* operator->(void) const
        {
            return GetNonNullPointer();
        }

    /// Dereference operator returning pointer.
    ///
    /// @sa
    ///   GetPointer()
    inline
    operator TObjectType*(void)
        {
            return GetPointerOrNull();
        }
    
    /// Dereference operator returning pointer -- constant version.
    ///
    /// @sa
    ///   GetPointer()
    inline
    operator const TObjectType*(void) const
        {
            return GetPointerOrNull();
        }
    
    /// Get pointer value and throw a null pointer exception if pointer
    /// is null.
    ///
    /// Similar to GetPointerOrNull() except that this method throws a null
    /// pointer exception if pointer is null, whereas GetPointerOrNull()
    /// returns a null value.
    ///
    /// @sa
    ///   GetPointerOrNull(), GetPointer(), GetObject()
    inline
    TObjectType* GetNonNullNCPointer(void) const
        {
            TObjectType* ptr = m_Data.second();
            if ( !ptr ) {
                ThrowNullPointerException();
            }
            return ptr;
        }

    /// Get pointer value.
    ///
    /// Similar to GetNonNullPointer() except that this method returns a null
    /// if the pointer is null, whereas GetNonNullPointer() throws a null
    /// pointer exception.
    ///
    /// @sa
    ///   GetNonNullPointer()
    inline
    TObjectType* GetNCPointerOrNull(void) const THROWS_NONE
        {
            return m_Data.second();
        }

    /// Get pointer,
    ///
    /// Same as GetPointerOrNull().
    ///
    /// @sa
    ///   GetPointerOrNull()
    inline
    TObjectType* GetNCPointer(void) const THROWS_NONE
        {
            return GetNCPointerOrNull();
        }

    /// Get object.
    ///
    /// Similar to GetNonNullPointer(), except that this method returns the
    /// object whereas GetNonNullPointer() returns a pointer to the object.
    /// 
    /// @sa
    ///   GetNonNullPointer()
    inline
    TObjectType& GetNCObject(void) const
        {
            return *GetNonNullNCPointer();
        }

private:
    // lock after construction from ptr
    void x_LockFromPtr()
        {
            if ( TObjectType* ptr = m_Data.second() ) {
                m_Data.first().Lock(ptr);
            }
        }
    // lock after construction from another ref
    void x_LockFromRef()
        {
            if ( TObjectType* ptr = m_Data.second() ) {
                m_Data.first().Relock(ptr);
            }
        }
    // lock after move construction from another ref
    void x_LockFromMoveConstructor(const Locker& src_locker)
        {
            if ( TObjectType* ptr = m_Data.second() ) {
                m_Data.first().TransferLock(ptr, src_locker);
            }
        }
    // assign from another ref
    void x_AssignFromRef(TObjectType* newPtr)
        {
            TObjectType* oldPtr = m_Data.second();
            if ( newPtr ) {
                m_Data.first().Relock(newPtr);
            }
            m_Data.second() = newPtr;
            if ( oldPtr ) {
                m_Data.first().Unlock(oldPtr);
            }
        }
    // move-assign from another ref
    void x_MoveAssign(const Locker& src_locker, TObjectType* newPtr)
        {
            TObjectType* oldPtr = m_Data.second();
            if ( newPtr ) {
                m_Data.first().TransferLock(newPtr, src_locker);
            }
            m_Data.second() = newPtr;
            if ( oldPtr ) {
                m_Data.first().Unlock(oldPtr);
            }
        }

    TObjectType* AtomicSwap(TObjectType* ptr)
        {
            // MIPSpro won't accept static_cast for some reason.
            return reinterpret_cast<TObjectType*>
                (SwapPointers(const_cast<void*volatile*>(
                                  reinterpret_cast<void**>(&m_Data.second())),
                              ptr));
        }

    pair_base_member<locker_type, TObjectType*> m_Data; ///< Pointer to object
    
private:
// Hide incorrect operators
    void operator-(TObjectType*) const;
    void operator-(int) const;
    void operator+(int) const;
};


/////////////////////////////////////////////////////////////////////////////
///
/// CConstRef --
///
/// Define a template class that stores a pointer to an object and defines
/// methods for constant referencing of object. 

template<class C, class Locker>
class CConstRef {
public:
    typedef C element_type;                 ///< Define alias element_type
    typedef const element_type TObjectType; ///< Define alias TObjectType
    typedef Locker locker_type;             ///< Define alias for locking type
    typedef CConstRef<C, Locker> TThisType; ///< Alias for this template type

    /// Helper template to template enable methods only for derived types
    template<class T>
    using enable_if_derived =
        typename std::enable_if<std::is_convertible<T*, TObjectType*>::value>;
    template<class TDerived, class TLocker> friend class CConstRef;

    /// Constructor for null pointer.
    inline
    CConstRef(void) THROWS_NONE
        {
        }

    /// Constructor for ENull pointer.
    inline
    CConstRef(ENull /*null*/) THROWS_NONE
        {
        }

    /// Constructor for explicit type conversion from pointer to object.
    explicit CConstRef(TObjectType* ptr)
        : m_Data(locker_type(), ptr)
        {
            x_LockFromPtr();
        }

    /// Constructor for explicit type conversion from pointer to object.
    CConstRef(TObjectType* ptr, const locker_type& locker_value)
        : m_Data(locker_value, ptr)
        {
            x_LockFromPtr();
        }

    /// Constructor from an existing CConstRef object
    CConstRef(const TThisType& ref)
        : m_Data(ref.m_Data)
        {
            x_LockFromRef();
        }

    /// Constructor from an existing CConstRef object of derived type
    template<class TDerived,
             class = typename enable_if_derived<TDerived>::type>
    CConstRef(const CConstRef<TDerived, Locker>& ref)
        : m_Data(ref.GetLocker(), ref.GetPointerOrNull())
        {
            x_LockFromRef();
        }

    /// Move constructor from an existing CConstRef object
    CConstRef(TThisType&& ref)
        : m_Data(ref.m_Data)
        {
            x_LockFromMoveConstructor(ref.m_Data.first());
            ref.m_Data.second() = 0;
        }

    /// Move constructor from an existing CConstRef object of derived type
    template<class TDerived,
             class = typename enable_if_derived<TDerived>::type>
    CConstRef(CConstRef<TDerived, Locker>&& ref)
        : m_Data(ref.GetLocker(), ref.GetPointerOrNull())
        {
            x_LockFromMoveConstructor(ref.m_Data.first());
            ref.m_Data.second() = 0;
        }

    /// Constructor from an existing CRef object of derived type
    template<class TDerived,
             class = typename enable_if_derived<TDerived>::type>
    CConstRef(const CRef<TDerived, Locker>& ref)
        : m_Data(ref.GetLocker(), ref.GetPointerOrNull())
        {
            x_LockFromRef();
        }

    /// Move constructor from an existing CRef object of derived type
    template<class TDerived,
             class = typename enable_if_derived<TDerived>::type>
    CConstRef(CRef<TDerived, Locker>&& ref)
        : m_Data(ref.GetLocker(), ref.GetPointerOrNull())
        {
            x_LockFromMoveConstructor(ref.m_Data.first());
            ref.m_Data.second() = 0;
        }

    /// Destructor.
    ~CConstRef(void)
        {
            Reset();
        }
    
    /// Get reference to locker object
    const locker_type& GetLocker(void) const
        {
            return m_Data.first();
        }

    /// Check if CConstRef is empty -- not pointing to any object, which means
    /// having a null value. 
    ///
    /// @sa
    ///   Empty(), IsNull()
    bool operator!(void) const THROWS_NONE
        {
            return m_Data.second() == 0;
        }
    
    /// Check if CConstRef is empty -- not pointing to any object which means
    /// having a null value. 
    ///
    /// @sa
    ///   IsNull(), operator!()
    bool Empty(void) const THROWS_NONE
        {
            return m_Data.second() == 0;
        }

    /// Check if CConstRef is not empty -- pointing to an object and has
    /// a non-null value. 
    bool NotEmpty(void) const THROWS_NONE
        {
            return m_Data.second() != 0;
        }

    /// Check if pointer is null -- same effect as Empty().
    ///
    /// @sa
    ///   Empty(), operator!()
    bool IsNull(void) const THROWS_NONE
        {
            return m_Data.second() == 0;
        }

    /// Check if pointer is not null -- same effect as NotEmpty().
    ///
    /// @sa
    ///   NotEmpty()
    bool NotNull(void) const THROWS_NONE
        {
            return m_Data.second() != 0;
        }

    /// Swaps the pointer with another reference
    ///
    /// @sa
    ///   Swap(ConstRef<>&)
    inline
    void Swap(TThisType& ref)
        {
            swap(m_Data, ref.m_Data);
            if ( TObjectType* ptr = m_Data.second() ) {
                m_Data.first().TransferLock(ptr, ref.m_Data.first());
            }
            if ( TObjectType* ptr = ref.m_Data.second() ) {
                ref.m_Data.first().TransferLock(ptr, m_Data.first());
            }
        }

    /// Reset reference object.
    ///
    /// This sets the pointer to object to null, and removes reference
    /// count to object and deletes the object if this is the last reference
    /// to the object.
    /// @sa
    ///   Reset(TObjectType*)
    inline
    void Reset(void)
        {
            TObjectType* ptr = m_Data.second();
            if ( ptr ) {
                m_Data.second() = 0;
                m_Data.first().Unlock(ptr);
            }
        }

    /// Reset reference object to new pointer.
    ///
    /// This sets the pointer to object to the new pointer, and removes
    /// reference count to old object and deletes the old object if this is
    /// the last reference to the old object.
    /// @sa
    ///   Reset()
    inline
    void Reset(TObjectType* newPtr)
        {
            TObjectType* oldPtr = m_Data.second();
            if ( newPtr != oldPtr ) {
                if ( newPtr ) {
                    m_Data.first().Lock(newPtr);
                }
                m_Data.second() = newPtr;
                if ( oldPtr ) {
                    m_Data.first().Unlock(oldPtr);
                }
            }
        }

    /// Release a reference to the object and return a pointer to the object.
    ///
    /// Releasing a reference means decreasing the reference count by "1". A
    /// pointer to the existing object is returned, unless this pointer is
    /// already null(0), in which case a null(0) is returned.
    ///
    /// Similar to Release(), except that this method returns a null,
    /// whereas Release() throws a null pointer exception.
    ///
    /// @sa
    ///   Release()
    inline
    TObjectType* ReleaseOrNull(void)
        {
            TObjectType* ptr = m_Data.second();
            if ( !ptr ) {
                return 0;
            }
            m_Data.second() = 0;
            m_Data.first().UnlockRelease(ptr);
            return ptr;
        }

    NCBI_NORETURN
    static void ThrowNullPointerException(void)
        {
            CObject::ThrowNullPointerException(/*typeid(TObjectType*)*/);
        }

    /// Release a reference to the object and return a pointer to the object.
    ///
    /// Releasing a reference means decreasing the reference count by "1". A
    /// pointer to the existing object is returned, unless this pointer is
    /// already null(0), in which the null pointer exception (eNullPtr) is
    /// thrown.
    ///
    /// Similar to ReleaseOrNull(), except that this method throws an exception
    /// whereas ReleaseOrNull() does not.
    ///
    /// @sa
    ///   ReleaseOrNull()
    inline
    TObjectType* Release(void)
        {
            TObjectType* ptr = m_Data.second();
            if ( !ptr ) {
                ThrowNullPointerException();
            }
            m_Data.second() = 0;
            m_Data.first().UnlockRelease(ptr);
            return ptr;
        }

    /// Reset reference object to new pointer.
    ///
    /// This sets the pointer to object to the new pointer, and removes
    /// reference count to old object and deletes the old object if this is
    /// the last reference to the old object.
    /// The new pointer is got from ref argument.
    /// Operation is atomic on this object, so that AtomicResetFrom() and
    /// AtomicReleaseTo() called from different threads will work properly.
    /// Operation is not atomic on ref argument.
    /// @sa
    ///   AtomicReleaseTo(CConstRef& ref);
    inline
    void AtomicResetFrom(const CConstRef& ref)
        {
            TObjectType* ptr = ref.m_Data.second();
            if ( ptr )
                m_Data.first().Lock(ptr); // for this
            TObjectType* old_ptr = AtomicSwap(ptr);
            if ( old_ptr )
                m_Data.first().Unlock(old_ptr);
        }
    /// Release referenced object to another CConstRef<> object.
    ///
    /// This copies the pointer to object to the argument ref,
    /// and release reference from this object.
    /// Old reference object held by argument ref is released and deleted if
    /// necessary.
    /// Operation is atomic on this object, so that AtomicResetFrom() and
    /// AtomicReleaseTo() called from different threads will work properly.
    /// Operation is not atomic on ref argument.
    /// @sa
    ///   AtomicResetFrom(const CConstRef& ref);
    inline
    void AtomicReleaseTo(CConstRef& ref)
        {
            TObjectType* old_ptr = AtomicSwap(0);
            if ( old_ptr ) {
                ref.Reset(old_ptr);
                m_Data.first().Unlock(old_ptr);
            }
            else {
                ref.Reset();
            }
        }

    /// Assignment operator for const references.
    TThisType& operator=(const TThisType& ref)
        {
            x_AssignFromRef(ref.m_Data.second());
            return *this;
        }

    /// Assignment operator for const references of derived types
    template<class TDerived,
             class = typename enable_if_derived<TDerived>::type>
    TThisType& operator=(const CConstRef<TDerived, Locker>& ref)
        {
            // we cannot use internals of CRef of derived type
            x_AssignFromRef(ref.m_Data.second());
            return *this;
        }

    /// Move assignment operator for const references.
    TThisType& operator=(TThisType&& ref)
        {
#ifdef NCBI_COMPILER_MSVC
            // extra check on MSVC
            if (this == &ref) {
                // no-op
                return *this;
            }
#endif
            x_MoveAssign(ref.m_Data.first(), ref.m_Data.second());
            ref.m_Data.second() = 0;
            return *this;
        }

    /// Move assignment operator for const references of derived types
    template<class TDerived,
             class = typename enable_if_derived<TDerived>::type>
    TThisType& operator=(CConstRef<TDerived, Locker>&& ref)
        {
            x_MoveAssign(ref.m_Data.first(), ref.m_Data.second());
            ref.m_Data.second() = 0;
            return *this;
        }

    /// Assignment operator for assigning a reference of derived type
    template<class TDerived,
             class = typename enable_if_derived<TDerived>::type>
    TThisType& operator=(const CRef<TDerived, Locker>& ref)
        {
            x_AssignFromRef(ref.m_Data.second());
            return *this;
        }

    /// Move assignment operator for assigning a reference of derived type
    template<class TDerived,
             class = typename enable_if_derived<TDerived>::type>
    TThisType& operator=(CRef<TDerived, Locker>&& ref)
        {
            x_MoveAssign(ref.m_Data.first(), ref.m_Data.second());
            ref.m_Data.second() = 0;
            return *this;
        }

    /// Assignment operator for const references with right hand side set to
    /// a pointer.
    TThisType& operator=(TObjectType* ptr)
        {
            Reset(ptr);
            return *this;
        }

    /// Assignment operator with right hand side set to ENull.
    TThisType& operator=(ENull /*null*/)
        {
            Reset();
            return *this;
        }

    /// Get pointer value and throw a null pointer exception if pointer
    /// is null.
    ///
    /// Similar to GetPointerOrNull() except that this method throws a null
    /// pointer exception if pointer is null, whereas GetPointerOrNull()
    /// returns a null value.
    ///
    /// @sa
    ///   GetPointerOrNull(), GetPointer(), GetObject()
    inline
    TObjectType* GetNonNullPointer(void) const
        {
            TObjectType* ptr = m_Data.second();
            if ( !ptr ) {
                ThrowNullPointerException();
            }
            return ptr;
        }

    /// Get pointer value.
    ///
    /// Similar to GetNonNullPointer() except that this method returns a null
    /// if the pointer is null, whereas GetNonNullPointer() throws a null
    /// pointer exception.
    ///
    /// @sa
    ///   GetNonNullPointer()
    inline
    TObjectType* GetPointerOrNull(void) const THROWS_NONE
        {
            return m_Data.second();
        }

    /// Get pointer,
    ///
    /// Same as GetPointerOrNull().
    ///
    /// @sa
    ///   GetPointerOrNull()
    inline
    TObjectType* GetPointer(void) const THROWS_NONE
        {
            return GetPointerOrNull();
        }

    /// Get object.
    ///
    /// Similar to GetNonNullPointer(), except that this method returns the
    /// object whereas GetNonNullPointer() returns a pointer to the object.
    /// 
    /// @sa
    ///   GetNonNullPointer()
    inline
    TObjectType& GetObject(void) const
        {
            return *GetNonNullPointer();
        }

    /// Dereference operator returning object.
    ///
    /// @sa
    ///   GetObject()
    inline
    TObjectType& operator*(void) const
        {
            return *GetNonNullPointer();
        }

    /// Reference operator.
    ///
    /// @sa
    ///   GetPointer()
    inline
    TObjectType* operator->(void) const
        {
            return GetNonNullPointer();
        }

    /// Dereference operator returning pointer.
    ///
    /// @sa
    ///   GetPointer()
    inline
    operator TObjectType*(void) const
        {
            return GetPointerOrNull();
        }

private:
    // lock after construction from ptr
    void x_LockFromPtr()
        {
            if ( TObjectType* ptr = m_Data.second() ) {
                m_Data.first().Lock(ptr);
            }
        }
    // lock after construction from another ref
    void x_LockFromRef()
        {
            if ( TObjectType* ptr = m_Data.second() ) {
                m_Data.first().Relock(ptr);
            }
        }
    // lock after move construction from another ref
    void x_LockFromMoveConstructor(const Locker& src_locker)
        {
            if ( TObjectType* ptr = m_Data.second() ) {
                m_Data.first().TransferLock(ptr, src_locker);
            }
        }
    // assign from another ref
    void x_AssignFromRef(TObjectType* newPtr)
        {
            TObjectType* oldPtr = m_Data.second();
            if ( newPtr ) {
                m_Data.first().Relock(newPtr);
            }
            m_Data.second() = newPtr;
            if ( oldPtr ) {
                m_Data.first().Unlock(oldPtr);
            }
        }
    // move-assign from another ref
    void x_MoveAssign(const Locker& src_locker, TObjectType* newPtr)
        {
            TObjectType* oldPtr = m_Data.second();
            if ( newPtr ) {
                m_Data.first().TransferLock(newPtr, src_locker);
            }
            m_Data.second() = newPtr;
            if ( oldPtr ) {
                m_Data.first().Unlock(oldPtr);
            }
        }

    TObjectType* AtomicSwap(TObjectType* ptr)
        {
            // MIPSpro won't accept static_cast for some reason.
            return reinterpret_cast<TObjectType*>
                (SwapPointers(const_cast<void*volatile*>(
                                  const_cast<void**>(
                                      reinterpret_cast<const void**>(&m_Data.second()))),
                              const_cast<C*>(ptr)));
        }

    pair_base_member<locker_type, TObjectType*> m_Data; ///< Pointer to object
    
private:
// Hide incorrect operators
    void operator-(TObjectType*) const;
    void operator-(int) const;
    void operator+(int) const;
};


/////////////////////////////////////////////////////////////////////////////
/// Comparison operators for CRef<> and CConstRef<> with ENull

/// Template operator == function for CRef objects -- rhs is null.
template<class T, class L>
inline
bool operator== (const CRef<T,L>& r1, ENull /*null*/)
{
    return r1.IsNull();
}

/// Template operator == function for CRef objects -- lhs is null.
template<class T, class L>
inline
bool operator== (ENull /*null*/, const CRef<T,L>& r1)
{
    return r1.IsNull();
}

/// Template operator != function for CRef objects -- rhs is null.
template<class T, class L>
inline
bool operator!= (const CRef<T,L>& r1, ENull /*null*/)
{
    return !r1.IsNull();
}

/// Template operator != function for CRef objects -- lhs is null.
template<class T, class L>
inline
bool operator!= (ENull /*null*/, const CRef<T,L>& r1)
{
    return !r1.IsNull();
}

/// Template operator == function for CConstRef objects -- rhs is null.
template<class T, class L>
inline
bool operator== (const CConstRef<T,L>& r1, ENull /*null*/)
{
    return r1.IsNull();
}

/// Template operator == function for CConstRef objects -- lhs is null.
template<class T, class L>
inline
bool operator== (ENull /*null*/, const CConstRef<T,L>& r1)
{
    return r1.IsNull();
}

/// Template operator != function for CConstRef objects -- rhs is null.
template<class T, class L>
inline
bool operator!= (const CConstRef<T,L>& r1, ENull /*null*/)
{
    return !r1.IsNull();
}

/// Template operator != function for CConstRef objects -- lhs is null.
template<class T, class L>
inline
bool operator!= (ENull /*null*/, const CConstRef<T,L>& r1)
{
    return !r1.IsNull();
}


#if defined(NCBI_COMPILER_WORKSHOP)
/// WorkShop fails to compare CRef/CConstRef via operator pointer sometimes.
/////////////////////////////////////////////////////////////////////////////
/// Comparison operators for CRef<>

/// Template operator < function for CRef objects.
template<class T, class L>
inline
bool operator< (const CRef<T,L>& r1, const CRef<T,L>& r2)
{
    return r1.GetPointerOrNull() < r2.GetPointerOrNull();
}

/// Template operator > function for CRef objects.
template<class T, class L>
inline
bool operator> (const CRef<T,L>& r1, const CRef<T,L>& r2)
{
    return r1.GetPointerOrNull() > r2.GetPointerOrNull();
}

/// Template operator < function for CRef objects.
template<class T, class L>
inline
bool operator<= (const CRef<T,L>& r1, const CRef<T,L>& r2)
{
    return r1.GetPointerOrNull() <= r2.GetPointerOrNull();
}

/// Template operator > function for CRef objects.
template<class T, class L>
inline
bool operator>= (const CRef<T,L>& r1, const CRef<T,L>& r2)
{
    return r1.GetPointerOrNull() >= r2.GetPointerOrNull();
}

/// Template operator == function for CRef objects.
template<class T, class L>
inline
bool operator== (const CRef<T,L>& r1, const CRef<T,L>& r2)
{
    return r1.GetPointerOrNull() == r2.GetPointerOrNull();
}

/// Template operator == function for CRef and CRef objects.
template<class T, class L>
inline
bool operator!= (const CRef<T,L>& r1, const CRef<T,L>& r2)
{
    return r1.GetPointerOrNull() != r2.GetPointerOrNull();
}

/////////////////////////////////////////////////////////////////////////////
/// Comparison operators for CConstRef<>

/// Template operator < function for CConstRef objects.
template<class T, class L>
inline
bool operator< (const CConstRef<T,L>& r1, const CConstRef<T,L>& r2)
{
    return r1.GetPointerOrNull() < r2.GetPointerOrNull();
}

/// Template operator > function for CConstRef objects.
template<class T, class L>
inline
bool operator> (const CConstRef<T,L>& r1, const CConstRef<T,L>& r2)
{
    return r1.GetPointerOrNull() > r2.GetPointerOrNull();
}

/// Template operator < function for CConstRef objects.
template<class T, class L>
inline
bool operator<= (const CConstRef<T,L>& r1, const CConstRef<T,L>& r2)
{
    return r1.GetPointerOrNull() <= r2.GetPointerOrNull();
}

/// Template operator > function for CConstRef objects.
template<class T, class L>
inline
bool operator>= (const CConstRef<T,L>& r1, const CConstRef<T,L>& r2)
{
    return r1.GetPointerOrNull() >= r2.GetPointerOrNull();
}

/// Template operator == function for CConstRef objects.
template<class T, class L>
inline
bool operator== (const CConstRef<T,L>& r1, const CConstRef<T,L>& r2)
{
    return r1.GetPointerOrNull() == r2.GetPointerOrNull();
}

/// Template operator != function for CConstRef objects.
template<class T, class L>
inline
bool operator!= (const CConstRef<T,L>& r1, const CConstRef<T,L>& r2)
{
    return r1.GetPointerOrNull() != r2.GetPointerOrNull();
}


/////////////////////////////////////////////////////////////////////////////
/// Mixed comparison operators

/// Template operator == function for CConstRef and CRef objects.
template<class T, class L>
inline
bool operator== (const CConstRef<T,L>& r1, const CRef<T,L>& r2)
{
    return r1.GetPointerOrNull() == r2.GetPointerOrNull();
}

/// Template operator == function for CRef and CConstRef objects.
template<class T, class L>
inline
bool operator== (const CRef<T,L>& r1, const CConstRef<T,L>& r2)
{
    return r1.GetPointerOrNull() == r2.GetPointerOrNull();
}

/// Template operator != function for CConstRef and CRef objects.
template<class T, class L>
inline
bool operator!= (const CConstRef<T,L>& r1, const CRef<T,L>& r2)
{
    return r1.GetPointerOrNull() != r2.GetPointerOrNull();
}

/// Template operator != function for CRef and CConstRef objects.
template<class T, class L>
inline
bool operator!= (const CRef<T,L>& r1, const CConstRef<T,L>& r2)
{
    return r1.GetPointerOrNull() != r2.GetPointerOrNull();
}
#endif


/////////////////////////////////////////////////////////////////////////////
/// Helper functions to get CRef<> and CConstRef<> objects

/// Template function for conversion of object pointer to CRef
template<class C>
inline
CRef<C> Ref(C* object)
{
    return CRef<C>(object);
}


/// Template function for conversion of const object pointer to CConstRef
template<class C>
inline
CConstRef<C> ConstRef(const C* object)
{
    return CConstRef<C>(object);
}



template<class Interface, class Locker = CInterfaceObjectLocker<Interface> >
class CIRef : public CRef<Interface, Locker>
{
    typedef CRef<Interface, Locker> TParent;
public:
    typedef typename TParent::TObjectType TObjectType;
    typedef typename TParent::locker_type locker_type;
    typedef CIRef<Interface, Locker> TThisType;

    /// Helper template to template enable methods only for derived types
    template<class T>
    using enable_if_derived =
        typename std::enable_if<std::is_convertible<T*, TObjectType*>::value>;
    
    // We have to redefine all constructors and assignment operators

    /// Constructor for null pointer.
    CIRef(void) THROWS_NONE
        {
        }

    /// Constructor for ENull pointer.
    CIRef(ENull /*null*/) THROWS_NONE
        {
        }

    /// Constructor for explicit type conversion from pointer to object.
    explicit CIRef(TObjectType* ptr)
        : TParent(ptr)
        {
        }

    /// Constructor from existing ref
    CIRef(const TThisType& ref)
        : TParent(ref)
        {
        }
    
    /// Constructor from existing ref of derived type
    template<class TDerived,
             class = typename enable_if_derived<TDerived>::type>
    CIRef(const CIRef<TDerived>& ref)
        : TParent(ref.GetNCPointerOrNull())
        {
        }

    /// Move constructor from existing ref
    CIRef(TThisType&& ref)
        : TParent(static_cast<TParent&&>(ref))
        {
        }
    
    /// Move constructor from existing ref of derived type
    template<class TDerived,
             class = typename enable_if_derived<TDerived>::type>
    CIRef(CIRef<TDerived>&& ref)
        : TParent(ref.GetPointerOrNull())
        {
            ref.ReleaseOrNull();
        }

    /// Constructor for explicit type conversion from pointer to object.
    CIRef(TObjectType* ptr, const locker_type& locker_value)
        : TParent(ptr, locker_value)
        {
        }

    /// Assignment operator for references.
    TThisType& operator=(const TThisType& ref)
        {
            TParent::operator=(ref);
            return *this;
        }

    /// Assignment operator for references of derived types
    template<class TDerived,
             class = typename enable_if_derived<TDerived>::type>
    TThisType& operator=(const CIRef<TDerived>& ref)
        {
            TParent::operator=(ref.GetNCPointerOrNull());
            return *this;
        }

    /// Move assignment operator for references.
    TThisType& operator=(TThisType&& ref)
        {
            TParent::operator=(static_cast<TParent&&>(ref));
            return *this;
        }

    /// Move assignment operator for references of derived types
    template<class TDerived,
             class = typename enable_if_derived<TDerived>::type>
    TThisType& operator=(CIRef<TDerived>&& ref)
        {
            TParent::operator=(ref.GetNCPointerOrNull());
            ref.ReleaseOrNull();
            return *this;
        }

    /// Assignment operator for references with right hand side set to
    /// a pointer.
    TThisType& operator=(TObjectType* ptr)
        {
            TParent::operator=(ptr);
            return *this;
        }

    /// Assignment operator with right hand side set to ENull.
    TThisType& operator=(ENull null)
        {
            TParent::operator=(null);
            return *this;
        }

    /// Swaps the pointer with another reference
    ///
    /// @sa
    ///   Swap(CRef<>&)
    void Swap(TThisType& ref)
        {
            TParent::Swap(ref);
        }
};


template<class Interface, class Locker = CInterfaceObjectLocker<Interface> >
class CConstIRef : public CConstRef<Interface, Locker>
{
    typedef CConstRef<Interface, Locker> TParent;
public:
    typedef typename TParent::TObjectType TObjectType;
    typedef typename TParent::locker_type locker_type;
    typedef CConstIRef<Interface, Locker> TThisType;

    /// Helper template to template enable methods only for derived types
    template<class T>
    using enable_if_derived =
        typename std::enable_if<std::is_convertible<T*, TObjectType*>::value>;
    
    // We have to redefine all constructors and assignment operators

    /// Constructor for null pointer.
    CConstIRef(void) THROWS_NONE
        {
        }

    /// Constructor for ENull pointer.
    CConstIRef(ENull /*null*/) THROWS_NONE
        {
        }

    /// Constructor for explicit type conversion from pointer to object.
    explicit CConstIRef(TObjectType* ptr)
        : TParent(ptr)
        {
        }

    /// Constructor for explicit type conversion from pointer to object.
    CConstIRef(TObjectType* ptr, const locker_type& locker_value)
        : TParent(ptr, locker_value)
        {
        }

    /// Constructor from an existing CConstRef object
    CConstIRef(const TThisType& ref)
        : TParent(ref)
        {
        }
    
    /// Constructor from an existing CConstRef object of derived type
    template<class TDerived,
             class = typename enable_if_derived<TDerived>::type>
    CConstIRef(const CConstIRef<TDerived>& ref)
        : TParent(ref.GetPointerOrNull())
        {
        }

    /// Move constructor from an existing CConstRef object
    CConstIRef(TThisType&& ref)
        : TParent(static_cast<TParent&&>(ref))
        {
        }
    
    /// Move constructor from an existing CConstRef object of derived type
    template<class TDerived,
             class = typename enable_if_derived<TDerived>::type>
    CConstIRef(CConstIRef<TDerived>&& ref)
        : TParent(ref.GetPointerOrNull())
        {
            ref.ReleaseOrNull();
        }

    /// Constructor from an existing CRef object of derived type
    template<class TDerived,
             class = typename enable_if_derived<TDerived>::type>
    CConstIRef(const CIRef<TDerived>& ref)
        : TParent(ref.GetPointerOrNull())
        {
        }

    /// Move constructor from an existing CRef object of derived type
    template<class TDerived,
             class = typename enable_if_derived<TDerived>::type>
    CConstIRef(CIRef<TDerived>&& ref)
        : TParent(ref.GetPointerOrNull())
        {
            ref.ReleaseOrNull();
        }

    /// Assignment operator for references.
    TThisType& operator=(const TThisType& ref)
        {
            TParent::operator=(ref);
            return *this;
        }

    /// Assignment operator for references of derived types
    template<class TDerived,
             class = typename enable_if_derived<TDerived>::type>
    TThisType& operator=(const CConstIRef<TDerived>& ref)
        {
            TParent::operator=(ref.GetPointerOrNull());
            return *this;
        }

    /// Move assignment operator for references.
    TThisType& operator=(TThisType&& ref)
        {
            TParent::operator=(static_cast<TParent&&>(ref));
            return *this;
        }

    /// Move assignment operator for references of derived types
    template<class TDerived,
             class = typename enable_if_derived<TDerived>::type>
    TThisType& operator=(CConstIRef<TDerived>&& ref)
        {
            TParent::operator=(ref.GetPointerOrNull());
            ref.ReleaseOrNull();
            return *this;
        }

    /// Assignment operator for assigning a reference to a const reference.
    template<class TDerived,
             class = typename enable_if_derived<TDerived>::type>
    TThisType& operator=(const CIRef<TDerived>& ref)
        {
            TParent::operator=(ref.GetPointerOrNull());
            return *this;
        }

    /// Move assignment operator for assigning a reference to a const reference.
    template<class TDerived,
             class = typename enable_if_derived<TDerived>::type>
    TThisType& operator=(CIRef<TDerived>&& ref)
        {
            TParent::operator=(ref.GetPointerOrNull());
            ref.ReleaseOrNull();
            return *this;
        }

    /// Assignment operator for references with right hand side set to
    /// a pointer.
    TThisType& operator=(TObjectType* ptr)
        {
            TParent::operator=(ptr);
            return *this;
        }

    /// Assignment operator with right hand side set to ENull.
    TThisType& operator=(ENull null)
        {
            TParent::operator=(null);
            return *this;
        }

    /// Swaps the pointer with another reference
    ///
    /// @sa
    ///   Swap(CRef<>&)
    void Swap(TThisType& ref)
        {
            TParent::Swap(ref);
        }
};


/////////////////////////////////////////////////////////////////////////////
///
/// CObjectFor --
///
/// Define a template class whose template parameter is a standard data type 
/// that will be pointed to.
///
/// The template class defines a private data member of the same type as the
/// template parameter, and accessor methods GetData() to retrieve the value
/// of this private data member. The class is derived from CObject and
/// therefore inherits the reference counter defined in that class. In essence,
/// this class serves as a "wrapper" class for standard data types allowing
/// reference counted smart pointers to be used for standard data types. 

template<typename T>
class CObjectFor : public CObject
{
public:
    typedef T TObjectType;          ///< Define alias for template parameter

    template<class... Args>
    explicit CObjectFor(Args&&... args)
        : m_Data(std::forward<Args>(args)...)
        {
        }

    /// Get data as a reference.
    T& GetData(void)
        {
            return m_Data;
        }

    /// Get data as a reference -- const version.
    const T& GetData(void) const
        {
            return m_Data;
        }

    /// Operator () to get data -- same as GetData().
    ///
    /// @sa
    ///   GetData()
    operator T& (void)
        {
            return GetData();
        }

    /// Operator () to get data -- const version, same as GetData().
    ///
    /// @sa
    ///   GetData()
    operator const T& (void) const
        {
            return GetData();
        }

    /// Assignment operator.
    T& operator=(const T& data)
        {
            m_Data = data;
            return *this;
        }

private:
    T m_Data;               ///< Data member of template parameter type
};


// Forward declaration for CPtrToObjectProxy
class CWeakObject;

/////////////////////////////////////////////////////////////////////////////
///
/// CPtrToObjectProxy --
///
/// Proxy class used for building CWeakObject and CWeakPtr.
///
/// Contains pointers to both CObject and CWeakObject instances.
/// The pointers are set to NULL after the instances are destroyed.

class CPtrToObjectProxy : public CObject
{
public:
    CPtrToObjectProxy(CWeakObject* ptr);

    NCBI_XNCBI_EXPORT
    ~CPtrToObjectProxy(void);

    /// Set pointer to NULL from object's destructor.
    void Clear(void);

    /// Lock the object and return pointer to it.
    /// If object is already destroyed then return NULL.
    NCBI_XNCBI_EXPORT
    CObject* GetLockedObject(void);

    /// Report about trying to convert incompatible interface fo CObject
    NCBI_NORETURN NCBI_XNCBI_EXPORT
    static void ReportIncompatibleType(const type_info& type);

private:
    CObject*        m_Ptr;
    CWeakObject*    m_WeakPtr;

private:
    friend class CWeakObject;
    CObject*  x_UpdateCObjectPtr(void);
};




/////////////////////////////////////////////////////////////////////////////
///
/// CWeakObject --
///
/// The class contains additional information to allow weak references to it.
///
/// Note: If you want to have your class weak referenced you should derive
///       from both CObject and CWeakObject.
///
/// @sa CWeakRef<>

class CWeakObject
{
public:
    NCBI_XNCBI_EXPORT
    CWeakObject(void);

    NCBI_XNCBI_EXPORT
    virtual ~CWeakObject(void);

    /// Get pointer to proxy object containing pointer to this object
    CPtrToObjectProxy* GetPtrProxy(void) const;

protected:
    /// Method cleaning all CWeakRefs referencing at this moment to the object
    /// After calling to this method all existing CWeakRefs referencing to the
    /// object will return NULL, so it effectively will be equal to deleting
    /// the object. This method should be called from DeleteThis() if it is
    /// overridden, if it does not actually deletes the object and if you want
    /// it to behave the same way as default DeleteThis() does in relation to
    /// CWeakRefs.
    NCBI_XNCBI_EXPORT
    void CleanWeakRefs(void) const;

private:
    /// Add reference to the object in "weak" manner.
    /// If reference was added successfully returns TRUE.
    /// If the object is already destroying then returns FALSE.
    bool x_AddWeakReference(CObject* obj);

    friend class CPtrToObjectProxy;

    /// Proxy object with pointer to this instance
    mutable CRef<CPtrToObjectProxy> m_SelfPtrProxy;
};



////////////////////////////////////////////////////////////////////////////
// CPtrToObjectProxy and CWeakObject inline methods
////////////////////////////////////////////////////////////////////////////


inline
CObject* CPtrToObjectProxy::x_UpdateCObjectPtr(void)
{
    // Race condition is legal here. The worst that can happen is
    // that dynamic_cast<> can be called more than once.
    if ( !m_Ptr ) {
        m_Ptr = dynamic_cast<CObject*>(m_WeakPtr);
        if ( !m_Ptr )
            CObjectCounterLocker::ReportIncompatibleType(typeid(*m_WeakPtr));
    }
    return m_Ptr;
}


inline
CPtrToObjectProxy* CWeakObject::GetPtrProxy(void) const
{
    // The x_UpdateCObjectPtr() member will check that the object derives from
    // CObject.
    CObject*  object_ptr = m_SelfPtrProxy->x_UpdateCObjectPtr();

    // This tests that the object is controlled by CRef.
    if ( !object_ptr->Referenced() ) {
        NCBI_THROW(CObjectException, eNoRef,
                   "Weak referenced object must be managed by CRef");
    }
    return m_SelfPtrProxy.GetNCPointer();
}


/////////////////////////////////////////////////////////////////////////////
///
/// CObjectEx --
///
/// The extended CObject class not only counting references to it but
/// containing additional information to allow weak references to it.
///
/// NOTE: If your class is derived from CObjectEx, you override DeleteThis()
///       method, you do not actually delete the object in your implementation
///       of DeleteThis() and you do not want all existing CWeakRefs to
///       reference to this object any longer then you must call method
///       CleanWeakRefs().
///
/// @sa CWeakRef<>

class CObjectEx : public CObject,
                  public CWeakObject
{
public:
    CObjectEx(void)
    {
    }

    NCBI_XNCBI_EXPORT
    virtual ~CObjectEx(void);
};

/////////////////////////////////////////////////////////////////////////////
///
/// CWeakObjectLocker --
///
/// Default locker class for CWeakRef template

template <class C>
class CWeakObjectLocker : public CObjectCounterLocker
{
public:
    /// Type working as proxy storage for pointer to object
    typedef CPtrToObjectProxy       TPtrProxyType;
    /// Alias for this type
    typedef CWeakObjectLocker<C>    TThisType;

    /// Get proxy storage for pointer to object
    TPtrProxyType* GetPtrProxy(C* object) const
    {
        return object->GetPtrProxy();
    }

    /// Lock the object and return pointer to it stored in the proxy.
    /// If object is already destroyed then return NULL CRef.
    CRef<C, TThisType> GetLockedObject(TPtrProxyType* proxy) const
    {
        CRef<C, TThisType> ref;
        if ( CObject* object = proxy->GetLockedObject() ) {
            ref.Reset(static_cast<C*>(object));
            object->RemoveReference(); // remove extra lock from GetLockedObject()
        }
        return ref;
    }
};


/////////////////////////////////////////////////////////////////////////////
///
/// CWeakInterfaceLocker --
///
/// Default locker class for CWeakIRef template

template <class Interface>
class CWeakInterfaceLocker : public CInterfaceObjectLocker<Interface>
{
public:
    /// Type working as proxy storage for pointer to object
    typedef CPtrToObjectProxy                TPtrProxyType;
    /// Alias for this type
    typedef CWeakInterfaceLocker<Interface>  TThisType;

    /// Get proxy storage for pointer to object
    TPtrProxyType* GetPtrProxy(Interface* ptr) const
    {
        CWeakObject* object = dynamic_cast<CWeakObject*>(ptr);
        if (!object) {
            CPtrToObjectProxy::ReportIncompatibleType(typeid(*ptr));
        }
        return object->GetPtrProxy();
    }

    /// Lock the object and return pointer to it stored in the proxy.
    /// If object is already destroyed then return NULL.
    CIRef<Interface, TThisType> GetLockedObject(TPtrProxyType* proxy) const
    {
        // We are not checking that result of dynamic_cast is not null
        // because type compatibility is already checked in GetPtrProxy()
        // which always called first. Now we can be sure that this
        // cast will not return null.
        CIRef<Interface, TThisType> ref;
        if ( CObject* object = proxy->GetLockedObject() ) {
            ref.Reset(dynamic_cast<Interface*>(object));
            object->RemoveReference(); // remove extra lock from GetLockedObject()
        }
        return ref;
    }
};


/////////////////////////////////////////////////////////////////////////////
// Traits for default locker parameter in CWeakRef
/////////////////////////////////////////////////////////////////////////////

template <class C>
class CWeakLockerTraits
{
public:
    typedef CWeakObjectLocker<C>  TLockerType;
};

template<class Interface, class Locker>
class CWeakIRef;


/////////////////////////////////////////////////////////////////////////////
///
/// CWeakRef --
///
/// Holds a "weak" pointer to the object.
///
/// It contains the pointer to the CObjectEx but does not control the lifetime
/// of the object. Access to the object can be obtained only via method Lock()
/// which will return CRef to the object which will guarantee that object is
/// not destroyed yet and that it will not be destroyed at least until the
/// returned CRef is destroyed.
/// If the referenced object is already destroyed then Lock() will
/// return empty reference.
///
/// Parameter class C should be derived from CObjectEx class (or from both
/// CObject and CWeakObject classes).
/// A CWeakRef instance cannot be created for the objects for which there are
/// currently no CRefs (except if the object is allocated in the stack or
/// static memory).
///
/// @sa CObjectEx, CWeakObject, CObject

template<class C, class Locker = typename CWeakLockerTraits<C>::TLockerType>
class CWeakRef
{
public:
    typedef C element_type;                 ///< Define alias element_type
    typedef element_type TObjectType;       ///< Define alias TObjectType
    typedef Locker locker_type;             ///< Define alias for locking type
    typedef typename Locker::TPtrProxyType proxy_type;
                                            ///< Alias for pointer proxy type
    typedef CRef<C, Locker> TRefType;       ///< Alias for the CRef type
    typedef CWeakRef<C, Locker> TThisType;  ///< Alias for this type


    /// Constructor for null pointer
    CWeakRef(void)
    {
    }

    /// Constructor for ENull pointer
    CWeakRef(ENull /*null*/)
    {
    }

    /// Constructor for pointer to a particular object
    ///
    /// @param ptr  Pointer to the object whose life time is controlled by CRef
    explicit CWeakRef(TObjectType* ptr)
    {
        // The reset member function calls the locker GetPtrProxy()
        // where type checking is done. In case of problems an exception
        // will be generated.
        Reset(ptr);
    }

    /// Constructor for explicit type conversion from pointer to object
    ///
    /// @param ptr  Pointer to the object whose life time is controlled by CRef
    CWeakRef(TObjectType* ptr, const locker_type& locker_value)
        : m_Locker(locker_value)
    {
        // The reset member function calls the locker GetPtrProxy()
        // where type checking is done. In case of problems an exception
        // will be generated.
        Reset(ptr);
    }

    // Default copy constructor and copy assignment are ok


    /// Get reference to locker object
    const locker_type& GetLocker(void) const
    {
        return m_Locker;
    }

    /// Lock the object and return reference to it.
    /// If the refenced object is already deleted then return null reference.
    TRefType Lock(void) const
    {
        if (!m_Proxy)
            return null;

        return m_Locker.GetLockedObject(m_Proxy.GetNCPointer());
    }

    /// Reset the containing pointer to null
    void Reset(void)
    {
        m_Proxy.Reset();
    }

    /// Reset the containing pointer to null
    void Reset(ENull /*null*/)
    {
        m_Proxy.Reset();
    }

    /// Reset the containing pointer to another object
    void Reset(TObjectType* ptr)
    {
        if (ptr) {
            // GetPtrProxy() will make type checking i.e.
            // will make sure that the object derives from CObject
            // and that it is controlled by CRef.
            m_Proxy.Reset(m_Locker.GetPtrProxy(ptr));
        }
        else {
            m_Proxy.Reset();
        }
    }

    /// Swap values of this reference with another
    void Swap(TThisType& ref)
    {
        m_Proxy.Swap(ref.m_Proxy);
        swap(m_Locker, ref.m_Locker);
    }

    /// Assignment from ENull pointer
    TThisType& operator= (ENull /*null*/)
    {
        Reset();
        return *this;
    }

    /// Assignment from pointer to other object
    TThisType& operator= (TObjectType* ptr)
    {
        Reset(ptr);
        return *this;
    }

    /// operator== to compare with another CWeakRef
    bool operator== (const TThisType& right)
    {
        return m_Proxy == right.m_Proxy;
    }

    /// operator!= to compare with another CWeakRef
    bool operator!= (const TThisType& right)
    {
        return !(*this == right);
    }

private:
    friend class CWeakIRef<C, Locker>;

    CRef<proxy_type> m_Proxy;
    locker_type      m_Locker;
};


/////////////////////////////////////////////////////////////////////////////
///
/// CWeakIRef --
///
/// Template for "weak" pointer to the interface.
///
/// Contains the pointer to interface. Final object pointed by this reference
/// should have ability of dynamic cast to CObjectEx. Reference do not control
/// the lifetime of the object. Access to the interface can be obtained only
/// via method Lock() which will return CIRef to the object which will
/// guarantee that object is not destroyed yet and until received CIRef is not
/// destroyed. If contained interface is already destroyed at the time
/// of calling Lock() then method will return empty reference.
///
/// @sa CObjectEx, CWeakRef<>

template<class Interface, class Locker = CWeakInterfaceLocker<Interface> >
class CWeakIRef : public CWeakRef<Interface, Locker>
{
    typedef CWeakRef<Interface, Locker>   TParent;
public:
    typedef typename TParent::TObjectType TObjectType;
    typedef typename TParent::locker_type locker_type;
    typedef CIRef<Interface, Locker>      TRefType;
    typedef CWeakIRef<Interface, Locker>  TThisType;


    /// Constructor for null pointer
    CWeakIRef(void)
    {
    }

    /// Constructor for ENull pointer
    CWeakIRef(ENull /*null*/)
    {
    }

    /// Constructor for pointer to the interface
    explicit CWeakIRef(TObjectType* ptr)
        : TParent(ptr)
    {
    }

    /// Constructor for explicit type conversion from pointer to object.
    CWeakIRef(TObjectType* ptr, const locker_type& locker_value)
        : TParent(ptr, locker_value)
    {
    }

    // Default copy constructor and copy assignment are ok


    /// Assignment from ENull pointer
    TThisType& operator= (ENull null)
    {
        TParent::operator= (null);
        return *this;
    }

    /// Assignment from pointer to another interface
    TThisType& operator= (TObjectType* ptr)
    {
        TParent::operator= (ptr);
        return *this;
    }


    /// Swap values of this reference with another
    void Swap(TThisType& ref)
    {
        TParent::Swap(ref);
    }


    /// Lock the object and return reference to it.
    /// If the refenced object is already deleted then return null reference.
    TRefType Lock(void) const
    {
        if (!this->m_Proxy)
            return null;

        return this->m_Locker.GetLockedObject(this->m_Proxy.GetNCPointer());
    }
};


/* @} */


END_NCBI_SCOPE

BEGIN_STD_SCOPE

template<class C, class L>
inline
void swap(NCBI_NS_NCBI::CRef<C,L>& ref1,
          NCBI_NS_NCBI::CRef<C,L>& ref2)
{
    ref1.Swap(ref2);
}


template<class C, class L>
inline
void swap(NCBI_NS_NCBI::CConstIRef<C,L>& ref1,
          NCBI_NS_NCBI::CConstIRef<C,L>& ref2)
{
    ref1.Swap(ref2);
}


template<class C, class L>
inline
void swap(NCBI_NS_NCBI::CIRef<C,L>& ref1,
          NCBI_NS_NCBI::CIRef<C,L>& ref2)
{
    ref1.Swap(ref2);
}


template<class C, class L>
inline
void swap(NCBI_NS_NCBI::CConstRef<C,L>& ref1,
          NCBI_NS_NCBI::CConstRef<C,L>& ref2)
{
    ref1.Swap(ref2);
}


template<class C, class L>
inline
void swap(NCBI_NS_NCBI::CWeakRef<C,L>& ref1,
          NCBI_NS_NCBI::CWeakRef<C,L>& ref2)
{
    ref1.Swap(ref2);
}


template<class C, class L>
inline
void swap(NCBI_NS_NCBI::CWeakIRef<C,L>& ref1,
          NCBI_NS_NCBI::CWeakIRef<C,L>& ref2)
{
    ref1.Swap(ref2);
}


END_STD_SCOPE

#endif /* NCBIOBJ__HPP */
