// refptr.hpp - Macros for RefCntPtrs
//   These macros simplify the process of
// creating a new reference count pointer
// class for a new class type - instead of
// copying an existing one and changing the
// names with a text editor, the
// preprocessor is used to substitute the
// appropriate names.  This is basically an
// attempt to emulate class templates, until
// C++ compilers that implement templates
// are available.
//
//   There are two primary macros here - the
// first is the DECLARE macro, for the class
// declaration and inline functions, i.e.
// the stuff usually found in the ".hpp"
// file.  The DEFINE macro contains the
// definition of the non-inline member
// functions - the stuff usually found in
// the ".cpp" file.
//
//   To use these, simply create a class
// derived from RefCntItem, then put a
// RefCntPtrDECLARE() in an appropriate
// header file and a RefCntPtrDEFINE() in
// appropriate definition file (the class
// name is substituted for 'type').

#ifndef RefCntPtr_MACROS
 #define RefCntPtr_MACROS

 // this is used to create a name for
 // the class being defined, by
 // concatenating the original item
 // class name with "RefCntPtr"
 #define RefCntPtr(type) \
             type ## RefCntPtr


 // If DEBUG is defined, using a zero
 // pointer (an error) results in a
 // message.  An exit() statement
 // might also be appropriate.

 #ifdef DEBUG
  #include <iostream.h>
  #define RefCntPtrDebug(type)      \
    if (0 == p_ptr)                 \
      cout << "ERROR - using 0 "    \
       "RefCntPtr to " #type "\n";
 #else
  #define RefCntPtrDebug(type)
 #endif


 #define RefCntPtrDECLARE(type)         \
 class RefCntPtr(type)                  \
  {                                     \
   private:                             \
                                        \
    type * p_ptr;                       \
                                        \
    void p_inc (void);                  \
                                        \
    void p_decAndDelete (               \
            type * dest);               \
                                        \
    void p_assign (                     \
            type * rVal);               \
   public:                              \
    RefCntPtr(type) (void)              \
      : p_ptr(0) {;};                   \
                                        \
    RefCntPtr(type) (                   \
            const RefCntPtr(type)& in)  \
      : p_ptr (in.p_ptr)                \
      { p_inc(); };                     \
                                        \
    RefCntPtr(type) (                   \
            type * in)                  \
      : p_ptr(in)                       \
      { p_inc(); };                     \
                                        \
    ~RefCntPtr(type) (void)             \
      {p_decAndDelete(p_ptr);};         \
                                        \
    RefCntPtr(type) & operator = (      \
            RefCntPtr(type) & rVal)     \
      { p_assign (rVal.p_ptr);          \
        return *this; };                \
                                        \
    RefCntPtr(type) & operator = (      \
            type * rVal)                \
      { p_assign (rVal);                \
        return *this; };                \
                                        \
    type * operator -> (void)           \
      { RefCntPtrDebug(type)            \
        return p_ptr; };                \
                                        \
    type & operator * (void)            \
      { RefCntPtrDebug(type)            \
        return *p_ptr; };               \
                                        \
    /* see note */                      \
    operator int (void)                 \
      {return (p_ptr != 0);};           \
                                        \
   private:                             \
    int operator == (RefCntPtr(type)&); \
    int operator != (RefCntPtr(type)&); \
    int operator <  (RefCntPtr(type)&); \
    int operator <= (RefCntPtr(type)&); \
    int operator >  (RefCntPtr(type)&); \
    int operator >= (RefCntPtr(type)&); \
 };
// operator int() allows comparisons such as
//   if (ptr) {},
// which is great. Unfortunately, it also
// allows comparisons such as
//   if (ptr1 == ptr2) {}
// to compile, but not as expected
// - operator int() is first applied to
// both sides, then the comparison is made
// with the resulting integers.
//
// This behavior can be overridden by
// declaring operator==  - it has precedence
// here.  Since I am working in MS-DOS with
// its segmented addressing, I didn't want
// pointer comparisons, (they don't always
// work) so I made operator== and its
// companions private.  It would be simple
// to make them public and implement them
// if needed.


 #define RefCntPtrDEFINE(type)             \
 void RefCntPtr(type)::p_assign(           \
         type * rVal)                      \
                                           \
 /* the order of the calls to the       */ \
 /* increment and decrement functions   */ \
 /* is significant here.  If the rVal   */ \
 /* and current value point to the same */ \
 /* place, we could delete the item and */ \
 /* then discover we need it.           */ \
  {                                        \
   type * tmp = p_ptr;                     \
   p_ptr = rVal;                           \
   p_inc();                                \
   p_decAndDelete(tmp);                    \
  };                                       \
                                           \
 void RefCntPtr(type)::p_inc (void)        \
  {                                        \
   if (p_ptr)                              \
     p_ptr->incRefCnt();                   \
  };                                       \
                                           \
 void RefCntPtr(type)::p_decAndDelete (    \
         type * dest)                      \
  {                                        \
   if (dest)                               \
    {                                      \
     dest->decRefCnt();                    \
     if (0 == dest->refCnt())              \
       delete dest;                        \
    };                                     \
  };

#endif
