// ------------------------------- //
// -------- Start of File -------- //
// ------------------------------- //
// ----------------------------------------------------------- //
// C++ Header File Name: refcount.hpp 
// Compiler Used: MSVC40, DJGPP 2.7.2.1, GCC 2.7.2.1, HP CPP 10.24
// Produced By: Doug Gaer   
// File Creation Date: 02/03/1997  
// Date Last Modified: 08/05/1998
// Copyright (c) 1997 Douglas M. Gaer
// ----------------------------------------------------------- // 
// ---------- Include File Description and Details  ---------- // 
// ----------------------------------------------------------- // 
/*
The VBD C++ classes are copyright (c) 1997, by Douglas M. Gaer.
All those who put this code or its derivatives in a commercial
product MUST mention this copyright in their documentation for
users of the products in which this code or its derivative
classes are used. Otherwise, you have the freedom to redistribute
verbatim copies of this source code, adapt it to your specific
needs, or improve the code and release your improvements to the
public provided that the modified files carry prominent notices
stating that you changed the files and the date of any change.

THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND.
THE ENTIRE RISK OF THE QUALITY AND PERFORMANCE OF THIS SOFTWARE
IS WITH YOU. SHOULD ANY ELEMENT OF THIS SOFTWARE PROVE DEFECTIVE,
YOU WILL ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR, OR
CORRECTION.

Reference counting is a technique used to ensure the safe
deletion or modification of an object when a copy of the
object exists. In order to create reference counted objects,
a class must inherit the CountedObject class to embed a
reference count into each object. The RefCount class is used
to handle pointers to reference counted objects. It works by
storing pointers to objects in containers, rather than the
objects themselves. 
*/
// ----------------------------------------------------------- //   
#ifndef __REFCOUNT_HPP__
#define __REFCOUNT_HPP__

// Reference (C)ounted (O)bject Class
// Reference counting works by storing a refcount with each object.
// Each time the object is referenced by some other object or array
// the counter is incremented. When the entity is finished with the
// object the counter is decremented. When the counter goes to zero,
// it means that no entities are referencing the object, so it can
// be saftly deleted. 
class CountedObject
// Inherit this class to embed a reference count into each object
{
public:
  CountedObject() { refcount = 1; }
  virtual ~CountedObject() { } // Destructor provided for virtuality

public:
  void IncRefCount() { refcount++; }
  void DecRefCount() { refcount--; }
  int UnReferenced() { return refcount == 0; } // Return true if not referenced
  unsigned Count() { return refcount; }  // Returns object's refcount
  void Release() { refcount = 0; } // Resets the object's refcount
  
protected:
  unsigned refcount;
};

// NOTE: To avoid portability problems with template classes, enable
// the __NOT_USING_TEMPLATE_CLASS__ macro and directly code this
// class for the data type that needs to be reference counted.

// Default to using template class
#ifndef __NOT_USING_TEMPLATE_CLASS__
#define __USING_TEMPLATE_CLASS__
#endif

#ifdef __USING_TEMPLATE_CLASS__
// (R)eference (C)ount class
// The RefCount class is used to handle reference-counted objects.
// Reference counted pointers work like normal pointers, except
// they keep track of the number of references to the objects they
// point to. To set up a reference counted pointer to an object,
// allocate and initialize the object on the heap using the new
// operator, and then pass a pointer to the object in a call to
// the RefCount constructor.
template<class TYPE>
class RefCount
{
public:
  RefCount(TYPE *ptr = 0) { ObjectPtr = ptr; }
  ~RefCount() { Unbind(); }
  RefCount(const RefCount<TYPE> &ptr) { // Copy constructor
    // Called when a RefCount pointer is passed by value or copied.
    // Like normal pointers only the ObjectPtr is copied, not the
    // object pointed to by ObjectPtr. The aliasing that ocurrs at
    // this point is recorded by incrementing the reference count.
    Bind(ptr);
  }
    
  void operator=(const RefCount<TYPE> &ptr) {
    // Assignment operator using share semantics.
    // This assignment operator does not allow chain assignments
    NewBinding(ptr);
  }
  
  // int is a dummy parameter used to set the pointer to a NULL
  void operator=(int) { Unbind(); ObjectPtr = 0; }

protected:
  void Bind(const RefCount<TYPE> &ptr);
  void Unbind();
  void NewBinding(const RefCount<TYPE> &ptr);

protected:
  // TYPE assumed to be a structure having IncRefCount(), DecRefCount(),
  // and Referenced() member functions inherited from the CountedObject
  // class.
  TYPE *ObjectPtr;

public:
  // The overloaded * and -> operators allow reference counted pointers 
  // to be de-referenced like ordinary pointers.
  TYPE &operator*() const { return *ObjectPtr; } 
  TYPE *operator->() const { return ObjectPtr; }

  // Used to test whether a RefCount is null
  int operator!() { return ObjectPtr == 0; } // Return true if equal 0
  operator int() { return ObjectPtr != 0; }  // Return true if not equal 0
  
  friend int operator==(const RefCount<TYPE> &a, const RefCount<TYPE> &b)
  {
    return a.ObjectPtr == b.ObjectPtr;
  }
  
  friend int operator!=(const RefCount<TYPE> &a, const RefCount<TYPE> &b)
  {
    return a.ObjectPtr != b.ObjectPtr;
  }
};

template<class TYPE>
void RefCount<TYPE>::Bind(const RefCount<TYPE> &ptr)
// Binds object to the same object that ptr is bound to.
{
  ObjectPtr = ptr.ObjectPtr;
  if (ObjectPtr) ObjectPtr->IncRefCount();
}

template<class TYPE>
void RefCount<TYPE>::Unbind()
{
  // Ensure that ObjectPtr is pointing to an object
  if(ObjectPtr == 0) return; 

  ObjectPtr->DecRefCount();
  if (ObjectPtr->UnReferenced()) delete ObjectPtr;
}

template<class TYPE>
void RefCount<TYPE>::NewBinding(const RefCount<TYPE> &ptr)
// Gives the reference counted pointer a new binding (the same as ptr)
{
  if (ObjectPtr != ptr.ObjectPtr) { // Prevents accidental deletion
    Unbind();
    Bind(ptr);
  }
}

#endif // __USING_TEMPLATE_CLASS__

#endif // __REFCOUNT_HPP__
// ----------------------------------------------------------- //
// ------------------------------- //
// --------- End of File --------- //
// ------------------------------- //
