home *** CD-ROM | disk | FTP | other *** search
/ Supercompiler 1997 / SUPERCOMPILER97.iso / MS_VC.50 / VC / MFC / INCLUDE / AFXTEMPL.H < prev    next >
Encoding:
C/C++ Source or Header  |  1996-12-04  |  45.2 KB  |  1,676 lines

  1. // This is a part of the Microsoft Foundation Classes C++ library.
  2. // Copyright (C) 1992-1997 Microsoft Corporation
  3. // All rights reserved.
  4. //
  5. // This source code is only intended as a supplement to the
  6. // Microsoft Foundation Classes Reference and related
  7. // electronic documentation provided with the library.
  8. // See these sources for detailed information regarding the
  9. // Microsoft Foundation Classes product.
  10.  
  11. #ifndef __AFXTEMPL_H__
  12. #define __AFXTEMPL_H__
  13.  
  14. #ifndef __AFXPLEX_H__
  15.     #include <afxplex_.h>
  16. #endif
  17.  
  18. #ifdef _AFX_MINREBUILD
  19. #pragma component(minrebuild, off)
  20. #endif
  21. #ifndef _AFX_FULLTYPEINFO
  22. #pragma component(mintypeinfo, on)
  23. #endif
  24.  
  25. #ifdef _AFX_PACKING
  26. #pragma pack(push, _AFX_PACKING)
  27. #endif
  28.  
  29. #ifdef _DEBUG
  30. static char _szAfxTempl[] = "afxtempl.h";
  31. #undef THIS_FILE
  32. #define THIS_FILE _szAfxTempl
  33. #endif
  34.  
  35. #ifndef ALL_WARNINGS
  36. #pragma warning(disable: 4114)
  37. #endif
  38.  
  39. /////////////////////////////////////////////////////////////////////////////
  40. // global helpers (can be overridden)
  41.  
  42. #ifdef new
  43. #undef new
  44. #define _REDEF_NEW
  45. #endif
  46.  
  47. #ifndef _INC_NEW
  48.     #include <new.h>
  49. #endif
  50.  
  51. template<class TYPE>
  52. inline void AFXAPI ConstructElements(TYPE* pElements, int nCount)
  53. {
  54.     ASSERT(nCount == 0 ||
  55.         AfxIsValidAddress(pElements, nCount * sizeof(TYPE)));
  56.  
  57.     // first do bit-wise zero initialization
  58.     memset((void*)pElements, 0, nCount * sizeof(TYPE));
  59.  
  60.     // then call the constructor(s)
  61.     for (; nCount--; pElements++)
  62.         ::new((void*)pElements) TYPE;
  63. }
  64.  
  65. template<class TYPE>
  66. inline void AFXAPI DestructElements(TYPE* pElements, int nCount)
  67. {
  68.     ASSERT(nCount == 0 ||
  69.         AfxIsValidAddress(pElements, nCount * sizeof(TYPE)));
  70.  
  71.     // call the destructor(s)
  72.     for (; nCount--; pElements++)
  73.         pElements->~TYPE();
  74. }
  75.  
  76. template<class TYPE>
  77. inline void AFXAPI CopyElements(TYPE* pDest, const TYPE* pSrc, int nCount)
  78. {
  79.     ASSERT(nCount == 0 ||
  80.         AfxIsValidAddress(pDest, nCount * sizeof(TYPE)));
  81.     ASSERT(nCount == 0 ||
  82.         AfxIsValidAddress(pSrc, nCount * sizeof(TYPE)));
  83.  
  84.     // default is element-copy using assignment
  85.     while (nCount--)
  86.         *pDest++ = *pSrc++;
  87. }
  88.  
  89. template<class TYPE>
  90. void AFXAPI SerializeElements(CArchive& ar, TYPE* pElements, int nCount)
  91. {
  92.     ASSERT(nCount == 0 ||
  93.         AfxIsValidAddress(pElements, nCount * sizeof(TYPE)));
  94.  
  95.     // default is bit-wise read/write
  96.     if (ar.IsStoring())
  97.         ar.Write((void*)pElements, nCount * sizeof(TYPE));
  98.     else
  99.         ar.Read((void*)pElements, nCount * sizeof(TYPE));
  100. }
  101.  
  102. #ifdef _DEBUG
  103. template<class TYPE>
  104. void AFXAPI DumpElements(CDumpContext& dc, const TYPE* pElements, int nCount)
  105. {
  106.     ASSERT(nCount == 0 ||
  107.         AfxIsValidAddress(pElements, nCount * sizeof(TYPE), FALSE));
  108.     &dc; // not used
  109.     pElements;  // not used
  110.     nCount; // not used
  111.  
  112.     // default does nothing
  113. }
  114. #endif
  115.  
  116. template<class TYPE, class ARG_TYPE>
  117. BOOL AFXAPI CompareElements(const TYPE* pElement1, const ARG_TYPE* pElement2)
  118. {
  119.     ASSERT(AfxIsValidAddress(pElement1, sizeof(TYPE), FALSE));
  120.     ASSERT(AfxIsValidAddress(pElement2, sizeof(ARG_TYPE), FALSE));
  121.  
  122.     return *pElement1 == *pElement2;
  123. }
  124.  
  125. template<class ARG_KEY>
  126. inline UINT AFXAPI HashKey(ARG_KEY key)
  127. {
  128.     // default identity hash - works for most primitive values
  129.     return ((UINT)(void*)(DWORD)key) >> 4;
  130. }
  131.  
  132. // special versions for CString
  133. #if _MSC_VER >= 1100
  134. template<> void AFXAPI ConstructElements<CString> (CString* pElements, int nCount);
  135. template<> void AFXAPI DestructElements<CString> (CString* pElements, int nCount);
  136. template<> void AFXAPI CopyElements<CString> (CString* pDest, const CString* pSrc, int nCount);
  137. template<> void AFXAPI SerializeElements<CString> (CArchive& ar, CString* pElements, int nCount);
  138. #ifndef OLE2ANSI
  139. template<> UINT AFXAPI HashKey<LPCWSTR> (LPCWSTR key);
  140. #endif
  141. template<> UINT AFXAPI HashKey<LPCSTR> (LPCSTR key);
  142. #else // _MSC_VER >= 1100
  143. void AFXAPI ConstructElements(CString* pElements, int nCount);
  144. void AFXAPI DestructElements(CString* pElements, int nCount);
  145. void AFXAPI CopyElements(CString* pDest, const CString* pSrc, int nCount);
  146. void AFXAPI SerializeElements(CArchive& ar, CString* pElements, int nCount);
  147. #ifndef OLE2ANSI
  148. UINT AFXAPI HashKey(LPCWSTR key);
  149. #endif
  150. UINT AFXAPI HashKey(LPCSTR key);
  151. #endif // _MSC_VER >= 1100
  152.  
  153. // forward declarations
  154. class COleVariant;
  155. struct tagVARIANT;
  156.  
  157. // special versions for COleVariant
  158. #if _MSC_VER >= 1100
  159. template<> void AFXAPI ConstructElements<COleVariant> (COleVariant* pElements, int nCount);
  160. template<> void AFXAPI DestructElements<COleVariant> (COleVariant* pElements, int nCount);
  161. template<> void AFXAPI CopyElements<COleVariant> (COleVariant* pDest, const COleVariant* pSrc, int nCount);
  162. template<> void AFXAPI SerializeElements<COleVariant> (CArchive& ar, COleVariant* pElements, int nCount);
  163. #ifdef _DEBUG
  164. template<> void AFXAPI DumpElements<COleVariant> (CDumpContext& dc, const COleVariant* pElements, int nCount);
  165. #endif
  166. template<> UINT AFXAPI HashKey<const struct tagVARIANT&> (const struct tagVARIANT& var);
  167. #else // _MSC_VER >= 1100
  168. void AFXAPI ConstructElements(COleVariant* pElements, int nCount);
  169. void AFXAPI DestructElements(COleVariant* pElements, int nCount);
  170. void AFXAPI CopyElements(COleVariant* pDest, const COleVariant* pSrc, int nCount);
  171. void AFXAPI SerializeElements(CArchive& ar, COleVariant* pElements, int nCount);
  172. #ifdef _DEBUG
  173. void AFXAPI DumpElements(CDumpContext& dc, const COleVariant* pElements, int nCount);
  174. #endif
  175. UINT AFXAPI HashKey(const struct tagVARIANT& var);
  176. #endif // _MSC_VER >= 1100
  177.  
  178. #define new DEBUG_NEW
  179.  
  180. /////////////////////////////////////////////////////////////////////////////
  181. // CArray<TYPE, ARG_TYPE>
  182.  
  183. template<class TYPE, class ARG_TYPE>
  184. class CArray : public CObject
  185. {
  186. public:
  187. // Construction
  188.     CArray();
  189.  
  190. // Attributes
  191.     int GetSize() const;
  192.     int GetUpperBound() const;
  193.     void SetSize(int nNewSize, int nGrowBy = -1);
  194.  
  195. // Operations
  196.     // Clean up
  197.     void FreeExtra();
  198.     void RemoveAll();
  199.  
  200.     // Accessing elements
  201.     TYPE GetAt(int nIndex) const;
  202.     void SetAt(int nIndex, ARG_TYPE newElement);
  203.     TYPE& ElementAt(int nIndex);
  204.  
  205.     // Direct Access to the element data (may return NULL)
  206.     const TYPE* GetData() const;
  207.     TYPE* GetData();
  208.  
  209.     // Potentially growing the array
  210.     void SetAtGrow(int nIndex, ARG_TYPE newElement);
  211.     int Add(ARG_TYPE newElement);
  212.     int Append(const CArray& src);
  213.     void Copy(const CArray& src);
  214.  
  215.     // overloaded operator helpers
  216.     TYPE operator[](int nIndex) const;
  217.     TYPE& operator[](int nIndex);
  218.  
  219.     // Operations that move elements around
  220.     void InsertAt(int nIndex, ARG_TYPE newElement, int nCount = 1);
  221.     void RemoveAt(int nIndex, int nCount = 1);
  222.     void InsertAt(int nStartIndex, CArray* pNewArray);
  223.  
  224. // Implementation
  225. protected:
  226.     TYPE* m_pData;   // the actual array of data
  227.     int m_nSize;     // # of elements (upperBound - 1)
  228.     int m_nMaxSize;  // max allocated
  229.     int m_nGrowBy;   // grow amount
  230.  
  231. public:
  232.     ~CArray();
  233.     void Serialize(CArchive&);
  234. #ifdef _DEBUG
  235.     void Dump(CDumpContext&) const;
  236.     void AssertValid() const;
  237. #endif
  238. };
  239.  
  240. /////////////////////////////////////////////////////////////////////////////
  241. // CArray<TYPE, ARG_TYPE> inline functions
  242.  
  243. template<class TYPE, class ARG_TYPE>
  244. inline int CArray<TYPE, ARG_TYPE>::GetSize() const
  245.     { return m_nSize; }
  246. template<class TYPE, class ARG_TYPE>
  247. inline int CArray<TYPE, ARG_TYPE>::GetUpperBound() const
  248.     { return m_nSize-1; }
  249. template<class TYPE, class ARG_TYPE>
  250. inline void CArray<TYPE, ARG_TYPE>::RemoveAll()
  251.     { SetSize(0, -1); }
  252. template<class TYPE, class ARG_TYPE>
  253. inline TYPE CArray<TYPE, ARG_TYPE>::GetAt(int nIndex) const
  254.     { ASSERT(nIndex >= 0 && nIndex < m_nSize);
  255.         return m_pData[nIndex]; }
  256. template<class TYPE, class ARG_TYPE>
  257. inline void CArray<TYPE, ARG_TYPE>::SetAt(int nIndex, ARG_TYPE newElement)
  258.     { ASSERT(nIndex >= 0 && nIndex < m_nSize);
  259.         m_pData[nIndex] = newElement; }
  260. template<class TYPE, class ARG_TYPE>
  261. inline TYPE& CArray<TYPE, ARG_TYPE>::ElementAt(int nIndex)
  262.     { ASSERT(nIndex >= 0 && nIndex < m_nSize);
  263.         return m_pData[nIndex]; }
  264. template<class TYPE, class ARG_TYPE>
  265. inline const TYPE* CArray<TYPE, ARG_TYPE>::GetData() const
  266.     { return (const TYPE*)m_pData; }
  267. template<class TYPE, class ARG_TYPE>
  268. inline TYPE* CArray<TYPE, ARG_TYPE>::GetData()
  269.     { return (TYPE*)m_pData; }
  270. template<class TYPE, class ARG_TYPE>
  271. inline int CArray<TYPE, ARG_TYPE>::Add(ARG_TYPE newElement)
  272.     { int nIndex = m_nSize;
  273.         SetAtGrow(nIndex, newElement);
  274.         return nIndex; }
  275. template<class TYPE, class ARG_TYPE>
  276. inline TYPE CArray<TYPE, ARG_TYPE>::operator[](int nIndex) const
  277.     { return GetAt(nIndex); }
  278. template<class TYPE, class ARG_TYPE>
  279. inline TYPE& CArray<TYPE, ARG_TYPE>::operator[](int nIndex)
  280.     { return ElementAt(nIndex); }
  281.  
  282. /////////////////////////////////////////////////////////////////////////////
  283. // CArray<TYPE, ARG_TYPE> out-of-line functions
  284.  
  285. template<class TYPE, class ARG_TYPE>
  286. CArray<TYPE, ARG_TYPE>::CArray()
  287. {
  288.     m_pData = NULL;
  289.     m_nSize = m_nMaxSize = m_nGrowBy = 0;
  290. }
  291.  
  292. template<class TYPE, class ARG_TYPE>
  293. CArray<TYPE, ARG_TYPE>::~CArray()
  294. {
  295.     ASSERT_VALID(this);
  296.  
  297.     if (m_pData != NULL)
  298.     {
  299.         DestructElements<TYPE>(m_pData, m_nSize);
  300.         delete[] (BYTE*)m_pData;
  301.     }
  302. }
  303.  
  304. template<class TYPE, class ARG_TYPE>
  305. void CArray<TYPE, ARG_TYPE>::SetSize(int nNewSize, int nGrowBy)
  306. {
  307.     ASSERT_VALID(this);
  308.     ASSERT(nNewSize >= 0);
  309.  
  310.     if (nGrowBy != -1)
  311.         m_nGrowBy = nGrowBy;  // set new size
  312.  
  313.     if (nNewSize == 0)
  314.     {
  315.         // shrink to nothing
  316.         if (m_pData != NULL)
  317.         {
  318.             DestructElements<TYPE>(m_pData, m_nSize);
  319.             delete[] (BYTE*)m_pData;
  320.             m_pData = NULL;
  321.         }
  322.         m_nSize = m_nMaxSize = 0;
  323.     }
  324.     else if (m_pData == NULL)
  325.     {
  326.         // create one with exact size
  327. #ifdef SIZE_T_MAX
  328.         ASSERT(nNewSize <= SIZE_T_MAX/sizeof(TYPE));    // no overflow
  329. #endif
  330.         m_pData = (TYPE*) new BYTE[nNewSize * sizeof(TYPE)];
  331.         ConstructElements<TYPE>(m_pData, nNewSize);
  332.         m_nSize = m_nMaxSize = nNewSize;
  333.     }
  334.     else if (nNewSize <= m_nMaxSize)
  335.     {
  336.         // it fits
  337.         if (nNewSize > m_nSize)
  338.         {
  339.             // initialize the new elements
  340.             ConstructElements<TYPE>(&m_pData[m_nSize], nNewSize-m_nSize);
  341.         }
  342.         else if (m_nSize > nNewSize)
  343.         {
  344.             // destroy the old elements
  345.             DestructElements<TYPE>(&m_pData[nNewSize], m_nSize-nNewSize);
  346.         }
  347.         m_nSize = nNewSize;
  348.     }
  349.     else
  350.     {
  351.         // otherwise, grow array
  352.         int nGrowBy = m_nGrowBy;
  353.         if (nGrowBy == 0)
  354.         {
  355.             // heuristically determine growth when nGrowBy == 0
  356.             //  (this avoids heap fragmentation in many situations)
  357.             nGrowBy = m_nSize / 8;
  358.             nGrowBy = (nGrowBy < 4) ? 4 : ((nGrowBy > 1024) ? 1024 : nGrowBy);
  359.         }
  360.         int nNewMax;
  361.         if (nNewSize < m_nMaxSize + nGrowBy)
  362.             nNewMax = m_nMaxSize + nGrowBy;  // granularity
  363.         else
  364.             nNewMax = nNewSize;  // no slush
  365.  
  366.         ASSERT(nNewMax >= m_nMaxSize);  // no wrap around
  367. #ifdef SIZE_T_MAX
  368.         ASSERT(nNewMax <= SIZE_T_MAX/sizeof(TYPE)); // no overflow
  369. #endif
  370.         TYPE* pNewData = (TYPE*) new BYTE[nNewMax * sizeof(TYPE)];
  371.  
  372.         // copy new data from old
  373.         memcpy(pNewData, m_pData, m_nSize * sizeof(TYPE));
  374.  
  375.         // construct remaining elements
  376.         ASSERT(nNewSize > m_nSize);
  377.         ConstructElements<TYPE>(&pNewData[m_nSize], nNewSize-m_nSize);
  378.  
  379.         // get rid of old stuff (note: no destructors called)
  380.         delete[] (BYTE*)m_pData;
  381.         m_pData = pNewData;
  382.         m_nSize = nNewSize;
  383.         m_nMaxSize = nNewMax;
  384.     }
  385. }
  386.  
  387. template<class TYPE, class ARG_TYPE>
  388. int CArray<TYPE, ARG_TYPE>::Append(const CArray& src)
  389. {
  390.     ASSERT_VALID(this);
  391.     ASSERT(this != &src);   // cannot append to itself
  392.  
  393.     int nOldSize = m_nSize;
  394.     SetSize(m_nSize + src.m_nSize);
  395.     CopyElements<TYPE>(m_pData + nOldSize, src.m_pData, src.m_nSize);
  396.     return nOldSize;
  397. }
  398.  
  399. template<class TYPE, class ARG_TYPE>
  400. void CArray<TYPE, ARG_TYPE>::Copy(const CArray& src)
  401. {
  402.     ASSERT_VALID(this);
  403.     ASSERT(this != &src);   // cannot append to itself
  404.  
  405.     SetSize(src.m_nSize);
  406.     CopyElements<TYPE>(m_pData, src.m_pData, src.m_nSize);
  407. }
  408.  
  409. template<class TYPE, class ARG_TYPE>
  410. void CArray<TYPE, ARG_TYPE>::FreeExtra()
  411. {
  412.     ASSERT_VALID(this);
  413.  
  414.     if (m_nSize != m_nMaxSize)
  415.     {
  416.         // shrink to desired size
  417. #ifdef SIZE_T_MAX
  418.         ASSERT(m_nSize <= SIZE_T_MAX/sizeof(TYPE)); // no overflow
  419. #endif
  420.         TYPE* pNewData = NULL;
  421.         if (m_nSize != 0)
  422.         {
  423.             pNewData = (TYPE*) new BYTE[m_nSize * sizeof(TYPE)];
  424.             // copy new data from old
  425.             memcpy(pNewData, m_pData, m_nSize * sizeof(TYPE));
  426.         }
  427.  
  428.         // get rid of old stuff (note: no destructors called)
  429.         delete[] (BYTE*)m_pData;
  430.         m_pData = pNewData;
  431.         m_nMaxSize = m_nSize;
  432.     }
  433. }
  434.  
  435. template<class TYPE, class ARG_TYPE>
  436. void CArray<TYPE, ARG_TYPE>::SetAtGrow(int nIndex, ARG_TYPE newElement)
  437. {
  438.     ASSERT_VALID(this);
  439.     ASSERT(nIndex >= 0);
  440.  
  441.     if (nIndex >= m_nSize)
  442.         SetSize(nIndex+1, -1);
  443.     m_pData[nIndex] = newElement;
  444. }
  445.  
  446. template<class TYPE, class ARG_TYPE>
  447. void CArray<TYPE, ARG_TYPE>::InsertAt(int nIndex, ARG_TYPE newElement, int nCount /*=1*/)
  448. {
  449.     ASSERT_VALID(this);
  450.     ASSERT(nIndex >= 0);    // will expand to meet need
  451.     ASSERT(nCount > 0);     // zero or negative size not allowed
  452.  
  453.     if (nIndex >= m_nSize)
  454.     {
  455.         // adding after the end of the array
  456.         SetSize(nIndex + nCount, -1);   // grow so nIndex is valid
  457.     }
  458.     else
  459.     {
  460.         // inserting in the middle of the array
  461.         int nOldSize = m_nSize;
  462.         SetSize(m_nSize + nCount, -1);  // grow it to new size
  463.         // destroy intial data before copying over it
  464.         DestructElements<TYPE>(&m_pData[nOldSize], nCount);
  465.         // shift old data up to fill gap
  466.         memmove(&m_pData[nIndex+nCount], &m_pData[nIndex],
  467.             (nOldSize-nIndex) * sizeof(TYPE));
  468.  
  469.         // re-init slots we copied from
  470.         ConstructElements<TYPE>(&m_pData[nIndex], nCount);
  471.     }
  472.  
  473.     // insert new value in the gap
  474.     ASSERT(nIndex + nCount <= m_nSize);
  475.     while (nCount--)
  476.         m_pData[nIndex++] = newElement;
  477. }
  478.  
  479. template<class TYPE, class ARG_TYPE>
  480. void CArray<TYPE, ARG_TYPE>::RemoveAt(int nIndex, int nCount)
  481. {
  482.     ASSERT_VALID(this);
  483.     ASSERT(nIndex >= 0);
  484.     ASSERT(nCount >= 0);
  485.     ASSERT(nIndex + nCount <= m_nSize);
  486.  
  487.     // just remove a range
  488.     int nMoveCount = m_nSize - (nIndex + nCount);
  489.     DestructElements<TYPE>(&m_pData[nIndex], nCount);
  490.     if (nMoveCount)
  491.         memcpy(&m_pData[nIndex], &m_pData[nIndex + nCount],
  492.             nMoveCount * sizeof(TYPE));
  493.     m_nSize -= nCount;
  494. }
  495.  
  496. template<class TYPE, class ARG_TYPE>
  497. void CArray<TYPE, ARG_TYPE>::InsertAt(int nStartIndex, CArray* pNewArray)
  498. {
  499.     ASSERT_VALID(this);
  500.     ASSERT(pNewArray != NULL);
  501.     ASSERT_VALID(pNewArray);
  502.     ASSERT(nStartIndex >= 0);
  503.  
  504.     if (pNewArray->GetSize() > 0)
  505.     {
  506.         InsertAt(nStartIndex, pNewArray->GetAt(0), pNewArray->GetSize());
  507.         for (int i = 0; i < pNewArray->GetSize(); i++)
  508.             SetAt(nStartIndex + i, pNewArray->GetAt(i));
  509.     }
  510. }
  511.  
  512. template<class TYPE, class ARG_TYPE>
  513. void CArray<TYPE, ARG_TYPE>::Serialize(CArchive& ar)
  514. {
  515.     ASSERT_VALID(this);
  516.  
  517.     CObject::Serialize(ar);
  518.     if (ar.IsStoring())
  519.     {
  520.         ar.WriteCount(m_nSize);
  521.     }
  522.     else
  523.     {
  524.         DWORD nOldSize = ar.ReadCount();
  525.         SetSize(nOldSize, -1);
  526.     }
  527.     SerializeElements<TYPE>(ar, m_pData, m_nSize);
  528. }
  529.  
  530. #ifdef _DEBUG
  531. template<class TYPE, class ARG_TYPE>
  532. void CArray<TYPE, ARG_TYPE>::Dump(CDumpContext& dc) const
  533. {
  534.     CObject::Dump(dc);
  535.  
  536.     dc << "with " << m_nSize << " elements";
  537.     if (dc.GetDepth() > 0)
  538.     {
  539.         dc << "\n";
  540.         DumpElements<TYPE>(dc, m_pData, m_nSize);
  541.     }
  542.  
  543.     dc << "\n";
  544. }
  545.  
  546. template<class TYPE, class ARG_TYPE>
  547. void CArray<TYPE, ARG_TYPE>::AssertValid() const
  548. {
  549.     CObject::AssertValid();
  550.  
  551.     if (m_pData == NULL)
  552.     {
  553.         ASSERT(m_nSize == 0);
  554.         ASSERT(m_nMaxSize == 0);
  555.     }
  556.     else
  557.     {
  558.         ASSERT(m_nSize >= 0);
  559.         ASSERT(m_nMaxSize >= 0);
  560.         ASSERT(m_nSize <= m_nMaxSize);
  561.         ASSERT(AfxIsValidAddress(m_pData, m_nMaxSize * sizeof(TYPE)));
  562.     }
  563. }
  564. #endif //_DEBUG
  565.  
  566. /////////////////////////////////////////////////////////////////////////////
  567. // CList<TYPE, ARG_TYPE>
  568.  
  569. template<class TYPE, class ARG_TYPE>
  570. class CList : public CObject
  571. {
  572. protected:
  573.     struct CNode
  574.     {
  575.         CNode* pNext;
  576.         CNode* pPrev;
  577.         TYPE data;
  578.     };
  579. public:
  580. // Construction
  581.     CList(int nBlockSize = 10);
  582.  
  583. // Attributes (head and tail)
  584.     // count of elements
  585.     int GetCount() const;
  586.     BOOL IsEmpty() const;
  587.  
  588.     // peek at head or tail
  589.     TYPE& GetHead();
  590.     TYPE GetHead() const;
  591.     TYPE& GetTail();
  592.     TYPE GetTail() const;
  593.  
  594. // Operations
  595.     // get head or tail (and remove it) - don't call on empty list !
  596.     TYPE RemoveHead();
  597.     TYPE RemoveTail();
  598.  
  599.     // add before head or after tail
  600.     POSITION AddHead(ARG_TYPE newElement);
  601.     POSITION AddTail(ARG_TYPE newElement);
  602.  
  603.     // add another list of elements before head or after tail
  604.     void AddHead(CList* pNewList);
  605.     void AddTail(CList* pNewList);
  606.  
  607.     // remove all elements
  608.     void RemoveAll();
  609.  
  610.     // iteration
  611.     POSITION GetHeadPosition() const;
  612.     POSITION GetTailPosition() const;
  613.     TYPE& GetNext(POSITION& rPosition); // return *Position++
  614.     TYPE GetNext(POSITION& rPosition) const; // return *Position++
  615.     TYPE& GetPrev(POSITION& rPosition); // return *Position--
  616.     TYPE GetPrev(POSITION& rPosition) const; // return *Position--
  617.  
  618.     // getting/modifying an element at a given position
  619.     TYPE& GetAt(POSITION position);
  620.     TYPE GetAt(POSITION position) const;
  621.     void SetAt(POSITION pos, ARG_TYPE newElement);
  622.     void RemoveAt(POSITION position);
  623.  
  624.     // inserting before or after a given position
  625.     POSITION InsertBefore(POSITION position, ARG_TYPE newElement);
  626.     POSITION InsertAfter(POSITION position, ARG_TYPE newElement);
  627.  
  628.     // helper functions (note: O(n) speed)
  629.     POSITION Find(ARG_TYPE searchValue, POSITION startAfter = NULL) const;
  630.         // defaults to starting at the HEAD, return NULL if not found
  631.     POSITION FindIndex(int nIndex) const;
  632.         // get the 'nIndex'th element (may return NULL)
  633.  
  634. // Implementation
  635. protected:
  636.     CNode* m_pNodeHead;
  637.     CNode* m_pNodeTail;
  638.     int m_nCount;
  639.     CNode* m_pNodeFree;
  640.     struct CPlex* m_pBlocks;
  641.     int m_nBlockSize;
  642.  
  643.     CNode* NewNode(CNode*, CNode*);
  644.     void FreeNode(CNode*);
  645.  
  646. public:
  647.     ~CList();
  648.     void Serialize(CArchive&);
  649. #ifdef _DEBUG
  650.     void Dump(CDumpContext&) const;
  651.     void AssertValid() const;
  652. #endif
  653. };
  654.  
  655. /////////////////////////////////////////////////////////////////////////////
  656. // CList<TYPE, ARG_TYPE> inline functions
  657.  
  658. template<class TYPE, class ARG_TYPE>
  659. inline int CList<TYPE, ARG_TYPE>::GetCount() const
  660.     { return m_nCount; }
  661. template<class TYPE, class ARG_TYPE>
  662. inline BOOL CList<TYPE, ARG_TYPE>::IsEmpty() const
  663.     { return m_nCount == 0; }
  664. template<class TYPE, class ARG_TYPE>
  665. inline TYPE& CList<TYPE, ARG_TYPE>::GetHead()
  666.     { ASSERT(m_pNodeHead != NULL);
  667.         return m_pNodeHead->data; }
  668. template<class TYPE, class ARG_TYPE>
  669. inline TYPE CList<TYPE, ARG_TYPE>::GetHead() const
  670.     { ASSERT(m_pNodeHead != NULL);
  671.         return m_pNodeHead->data; }
  672. template<class TYPE, class ARG_TYPE>
  673. inline TYPE& CList<TYPE, ARG_TYPE>::GetTail()
  674.     { ASSERT(m_pNodeTail != NULL);
  675.         return m_pNodeTail->data; }
  676. template<class TYPE, class ARG_TYPE>
  677. inline TYPE CList<TYPE, ARG_TYPE>::GetTail() const
  678.     { ASSERT(m_pNodeTail != NULL);
  679.         return m_pNodeTail->data; }
  680. template<class TYPE, class ARG_TYPE>
  681. inline POSITION CList<TYPE, ARG_TYPE>::GetHeadPosition() const
  682.     { return (POSITION) m_pNodeHead; }
  683. template<class TYPE, class ARG_TYPE>
  684. inline POSITION CList<TYPE, ARG_TYPE>::GetTailPosition() const
  685.     { return (POSITION) m_pNodeTail; }
  686. template<class TYPE, class ARG_TYPE>
  687. inline TYPE& CList<TYPE, ARG_TYPE>::GetNext(POSITION& rPosition) // return *Position++
  688.     { CNode* pNode = (CNode*) rPosition;
  689.         ASSERT(AfxIsValidAddress(pNode, sizeof(CNode)));
  690.         rPosition = (POSITION) pNode->pNext;
  691.         return pNode->data; }
  692. template<class TYPE, class ARG_TYPE>
  693. inline TYPE CList<TYPE, ARG_TYPE>::GetNext(POSITION& rPosition) const // return *Position++
  694.     { CNode* pNode = (CNode*) rPosition;
  695.         ASSERT(AfxIsValidAddress(pNode, sizeof(CNode)));
  696.         rPosition = (POSITION) pNode->pNext;
  697.         return pNode->data; }
  698. template<class TYPE, class ARG_TYPE>
  699. inline TYPE& CList<TYPE, ARG_TYPE>::GetPrev(POSITION& rPosition) // return *Position--
  700.     { CNode* pNode = (CNode*) rPosition;
  701.         ASSERT(AfxIsValidAddress(pNode, sizeof(CNode)));
  702.         rPosition = (POSITION) pNode->pPrev;
  703.         return pNode->data; }
  704. template<class TYPE, class ARG_TYPE>
  705. inline TYPE CList<TYPE, ARG_TYPE>::GetPrev(POSITION& rPosition) const // return *Position--
  706.     { CNode* pNode = (CNode*) rPosition;
  707.         ASSERT(AfxIsValidAddress(pNode, sizeof(CNode)));
  708.         rPosition = (POSITION) pNode->pPrev;
  709.         return pNode->data; }
  710. template<class TYPE, class ARG_TYPE>
  711. inline TYPE& CList<TYPE, ARG_TYPE>::GetAt(POSITION position)
  712.     { CNode* pNode = (CNode*) position;
  713.         ASSERT(AfxIsValidAddress(pNode, sizeof(CNode)));
  714.         return pNode->data; }
  715. template<class TYPE, class ARG_TYPE>
  716. inline TYPE CList<TYPE, ARG_TYPE>::GetAt(POSITION position) const
  717.     { CNode* pNode = (CNode*) position;
  718.         ASSERT(AfxIsValidAddress(pNode, sizeof(CNode)));
  719.         return pNode->data; }
  720. template<class TYPE, class ARG_TYPE>
  721. inline void CList<TYPE, ARG_TYPE>::SetAt(POSITION pos, ARG_TYPE newElement)
  722.     { CNode* pNode = (CNode*) pos;
  723.         ASSERT(AfxIsValidAddress(pNode, sizeof(CNode)));
  724.         pNode->data = newElement; }
  725.  
  726. template<class TYPE, class ARG_TYPE>
  727. CList<TYPE, ARG_TYPE>::CList(int nBlockSize)
  728. {
  729.     ASSERT(nBlockSize > 0);
  730.  
  731.     m_nCount = 0;
  732.     m_pNodeHead = m_pNodeTail = m_pNodeFree = NULL;
  733.     m_pBlocks = NULL;
  734.     m_nBlockSize = nBlockSize;
  735. }
  736.  
  737. template<class TYPE, class ARG_TYPE>
  738. void CList<TYPE, ARG_TYPE>::RemoveAll()
  739. {
  740.     ASSERT_VALID(this);
  741.  
  742.     // destroy elements
  743.     CNode* pNode;
  744.     for (pNode = m_pNodeHead; pNode != NULL; pNode = pNode->pNext)
  745.         DestructElements<TYPE>(&pNode->data, 1);
  746.  
  747.     m_nCount = 0;
  748.     m_pNodeHead = m_pNodeTail = m_pNodeFree = NULL;
  749.     m_pBlocks->FreeDataChain();
  750.     m_pBlocks = NULL;
  751. }
  752.  
  753. template<class TYPE, class ARG_TYPE>
  754. CList<TYPE, ARG_TYPE>::~CList()
  755. {
  756.     RemoveAll();
  757.     ASSERT(m_nCount == 0);
  758. }
  759.  
  760. /////////////////////////////////////////////////////////////////////////////
  761. // Node helpers
  762. //
  763. // Implementation note: CNode's are stored in CPlex blocks and
  764. //  chained together. Free blocks are maintained in a singly linked list
  765. //  using the 'pNext' member of CNode with 'm_pNodeFree' as the head.
  766. //  Used blocks are maintained in a doubly linked list using both 'pNext'
  767. //  and 'pPrev' as links and 'm_pNodeHead' and 'm_pNodeTail'
  768. //   as the head/tail.
  769. //
  770. // We never free a CPlex block unless the List is destroyed or RemoveAll()
  771. //  is used - so the total number of CPlex blocks may grow large depending
  772. //  on the maximum past size of the list.
  773. //
  774.  
  775. template<class TYPE, class ARG_TYPE>
  776. CList<TYPE, ARG_TYPE>::CNode*
  777. CList<TYPE, ARG_TYPE>::NewNode(CList::CNode* pPrev, CList::CNode* pNext)
  778. {
  779.     if (m_pNodeFree == NULL)
  780.     {
  781.         // add another block
  782.         CPlex* pNewBlock = CPlex::Create(m_pBlocks, m_nBlockSize,
  783.                  sizeof(CNode));
  784.  
  785.         // chain them into free list
  786.         CNode* pNode = (CNode*) pNewBlock->data();
  787.         // free in reverse order to make it easier to debug
  788.         pNode += m_nBlockSize - 1;
  789.         for (int i = m_nBlockSize-1; i >= 0; i--, pNode--)
  790.         {
  791.             pNode->pNext = m_pNodeFree;
  792.             m_pNodeFree = pNode;
  793.         }
  794.     }
  795.     ASSERT(m_pNodeFree != NULL);  // we must have something
  796.  
  797.     CList::CNode* pNode = m_pNodeFree;
  798.     m_pNodeFree = m_pNodeFree->pNext;
  799.     pNode->pPrev = pPrev;
  800.     pNode->pNext = pNext;
  801.     m_nCount++;
  802.     ASSERT(m_nCount > 0);  // make sure we don't overflow
  803.  
  804.     ConstructElements<TYPE>(&pNode->data, 1);
  805.     return pNode;
  806. }
  807.  
  808. template<class TYPE, class ARG_TYPE>
  809. void CList<TYPE, ARG_TYPE>::FreeNode(CList::CNode* pNode)
  810. {
  811.     DestructElements<TYPE>(&pNode->data, 1);
  812.     pNode->pNext = m_pNodeFree;
  813.     m_pNodeFree = pNode;
  814.     m_nCount--;
  815.     ASSERT(m_nCount >= 0);  // make sure we don't underflow
  816.  
  817.     // if no more elements, cleanup completely
  818.     if (m_nCount == 0)
  819.         RemoveAll();
  820. }
  821.  
  822. template<class TYPE, class ARG_TYPE>
  823. POSITION CList<TYPE, ARG_TYPE>::AddHead(ARG_TYPE newElement)
  824. {
  825.     ASSERT_VALID(this);
  826.  
  827.     CNode* pNewNode = NewNode(NULL, m_pNodeHead);
  828.     pNewNode->data = newElement;
  829.     if (m_pNodeHead != NULL)
  830.         m_pNodeHead->pPrev = pNewNode;
  831.     else
  832.         m_pNodeTail = pNewNode;
  833.     m_pNodeHead = pNewNode;
  834.     return (POSITION) pNewNode;
  835. }
  836.  
  837. template<class TYPE, class ARG_TYPE>
  838. POSITION CList<TYPE, ARG_TYPE>::AddTail(ARG_TYPE newElement)
  839. {
  840.     ASSERT_VALID(this);
  841.  
  842.     CNode* pNewNode = NewNode(m_pNodeTail, NULL);
  843.     pNewNode->data = newElement;
  844.     if (m_pNodeTail != NULL)
  845.         m_pNodeTail->pNext = pNewNode;
  846.     else
  847.         m_pNodeHead = pNewNode;
  848.     m_pNodeTail = pNewNode;
  849.     return (POSITION) pNewNode;
  850. }
  851.  
  852. template<class TYPE, class ARG_TYPE>
  853. void CList<TYPE, ARG_TYPE>::AddHead(CList* pNewList)
  854. {
  855.     ASSERT_VALID(this);
  856.  
  857.     ASSERT(pNewList != NULL);
  858.     ASSERT_VALID(pNewList);
  859.  
  860.     // add a list of same elements to head (maintain order)
  861.     POSITION pos = pNewList->GetTailPosition();
  862.     while (pos != NULL)
  863.         AddHead(pNewList->GetPrev(pos));
  864. }
  865.  
  866. template<class TYPE, class ARG_TYPE>
  867. void CList<TYPE, ARG_TYPE>::AddTail(CList* pNewList)
  868. {
  869.     ASSERT_VALID(this);
  870.     ASSERT(pNewList != NULL);
  871.     ASSERT_VALID(pNewList);
  872.  
  873.     // add a list of same elements
  874.     POSITION pos = pNewList->GetHeadPosition();
  875.     while (pos != NULL)
  876.         AddTail(pNewList->GetNext(pos));
  877. }
  878.  
  879. template<class TYPE, class ARG_TYPE>
  880. TYPE CList<TYPE, ARG_TYPE>::RemoveHead()
  881. {
  882.     ASSERT_VALID(this);
  883.     ASSERT(m_pNodeHead != NULL);  // don't call on empty list !!!
  884.     ASSERT(AfxIsValidAddress(m_pNodeHead, sizeof(CNode)));
  885.  
  886.     CNode* pOldNode = m_pNodeHead;
  887.     TYPE returnValue = pOldNode->data;
  888.  
  889.     m_pNodeHead = pOldNode->pNext;
  890.     if (m_pNodeHead != NULL)
  891.         m_pNodeHead->pPrev = NULL;
  892.     else
  893.         m_pNodeTail = NULL;
  894.     FreeNode(pOldNode);
  895.     return returnValue;
  896. }
  897.  
  898. template<class TYPE, class ARG_TYPE>
  899. TYPE CList<TYPE, ARG_TYPE>::RemoveTail()
  900. {
  901.     ASSERT_VALID(this);
  902.     ASSERT(m_pNodeTail != NULL);  // don't call on empty list !!!
  903.     ASSERT(AfxIsValidAddress(m_pNodeTail, sizeof(CNode)));
  904.  
  905.     CNode* pOldNode = m_pNodeTail;
  906.     TYPE returnValue = pOldNode->data;
  907.  
  908.     m_pNodeTail = pOldNode->pPrev;
  909.     if (m_pNodeTail != NULL)
  910.         m_pNodeTail->pNext = NULL;
  911.     else
  912.         m_pNodeHead = NULL;
  913.     FreeNode(pOldNode);
  914.     return returnValue;
  915. }
  916.  
  917. template<class TYPE, class ARG_TYPE>
  918. POSITION CList<TYPE, ARG_TYPE>::InsertBefore(POSITION position, ARG_TYPE newElement)
  919. {
  920.     ASSERT_VALID(this);
  921.  
  922.     if (position == NULL)
  923.         return AddHead(newElement); // insert before nothing -> head of the list
  924.  
  925.     // Insert it before position
  926.     CNode* pOldNode = (CNode*) position;
  927.     CNode* pNewNode = NewNode(pOldNode->pPrev, pOldNode);
  928.     pNewNode->data = newElement;
  929.  
  930.     if (pOldNode->pPrev != NULL)
  931.     {
  932.         ASSERT(AfxIsValidAddress(pOldNode->pPrev, sizeof(CNode)));
  933.         pOldNode->pPrev->pNext = pNewNode;
  934.     }
  935.     else
  936.     {
  937.         ASSERT(pOldNode == m_pNodeHead);
  938.         m_pNodeHead = pNewNode;
  939.     }
  940.     pOldNode->pPrev = pNewNode;
  941.     return (POSITION) pNewNode;
  942. }
  943.  
  944. template<class TYPE, class ARG_TYPE>
  945. POSITION CList<TYPE, ARG_TYPE>::InsertAfter(POSITION position, ARG_TYPE newElement)
  946. {
  947.     ASSERT_VALID(this);
  948.  
  949.     if (position == NULL)
  950.         return AddTail(newElement); // insert after nothing -> tail of the list
  951.  
  952.     // Insert it before position
  953.     CNode* pOldNode = (CNode*) position;
  954.     ASSERT(AfxIsValidAddress(pOldNode, sizeof(CNode)));
  955.     CNode* pNewNode = NewNode(pOldNode, pOldNode->pNext);
  956.     pNewNode->data = newElement;
  957.  
  958.     if (pOldNode->pNext != NULL)
  959.     {
  960.         ASSERT(AfxIsValidAddress(pOldNode->pNext, sizeof(CNode)));
  961.         pOldNode->pNext->pPrev = pNewNode;
  962.     }
  963.     else
  964.     {
  965.         ASSERT(pOldNode == m_pNodeTail);
  966.         m_pNodeTail = pNewNode;
  967.     }
  968.     pOldNode->pNext = pNewNode;
  969.     return (POSITION) pNewNode;
  970. }
  971.  
  972. template<class TYPE, class ARG_TYPE>
  973. void CList<TYPE, ARG_TYPE>::RemoveAt(POSITION position)
  974. {
  975.     ASSERT_VALID(this);
  976.  
  977.     CNode* pOldNode = (CNode*) position;
  978.     ASSERT(AfxIsValidAddress(pOldNode, sizeof(CNode)));
  979.  
  980.     // remove pOldNode from list
  981.     if (pOldNode == m_pNodeHead)
  982.     {
  983.         m_pNodeHead = pOldNode->pNext;
  984.     }
  985.     else
  986.     {
  987.         ASSERT(AfxIsValidAddress(pOldNode->pPrev, sizeof(CNode)));
  988.         pOldNode->pPrev->pNext = pOldNode->pNext;
  989.     }
  990.     if (pOldNode == m_pNodeTail)
  991.     {
  992.         m_pNodeTail = pOldNode->pPrev;
  993.     }
  994.     else
  995.     {
  996.         ASSERT(AfxIsValidAddress(pOldNode->pNext, sizeof(CNode)));
  997.         pOldNode->pNext->pPrev = pOldNode->pPrev;
  998.     }
  999.     FreeNode(pOldNode);
  1000. }
  1001.  
  1002. template<class TYPE, class ARG_TYPE>
  1003. POSITION CList<TYPE, ARG_TYPE>::FindIndex(int nIndex) const
  1004. {
  1005.     ASSERT_VALID(this);
  1006.     ASSERT(nIndex >= 0);
  1007.  
  1008.     if (nIndex >= m_nCount)
  1009.         return NULL;  // went too far
  1010.  
  1011.     CNode* pNode = m_pNodeHead;
  1012.     while (nIndex--)
  1013.     {
  1014.         ASSERT(AfxIsValidAddress(pNode, sizeof(CNode)));
  1015.         pNode = pNode->pNext;
  1016.     }
  1017.     return (POSITION) pNode;
  1018. }
  1019.  
  1020. template<class TYPE, class ARG_TYPE>
  1021. POSITION CList<TYPE, ARG_TYPE>::Find(ARG_TYPE searchValue, POSITION startAfter) const
  1022. {
  1023.     ASSERT_VALID(this);
  1024.  
  1025.     CNode* pNode = (CNode*) startAfter;
  1026.     if (pNode == NULL)
  1027.     {
  1028.         pNode = m_pNodeHead;  // start at head
  1029.     }
  1030.     else
  1031.     {
  1032.         ASSERT(AfxIsValidAddress(pNode, sizeof(CNode)));
  1033.         pNode = pNode->pNext;  // start after the one specified
  1034.     }
  1035.  
  1036.     for (; pNode != NULL; pNode = pNode->pNext)
  1037.         if (CompareElements<TYPE>(&pNode->data, &searchValue))
  1038.             return (POSITION)pNode;
  1039.     return NULL;
  1040. }
  1041.  
  1042. template<class TYPE, class ARG_TYPE>
  1043. void CList<TYPE, ARG_TYPE>::Serialize(CArchive& ar)
  1044. {
  1045.     ASSERT_VALID(this);
  1046.  
  1047.     CObject::Serialize(ar);
  1048.  
  1049.     if (ar.IsStoring())
  1050.     {
  1051.         ar.WriteCount(m_nCount);
  1052.         for (CNode* pNode = m_pNodeHead; pNode != NULL; pNode = pNode->pNext)
  1053.         {
  1054.             ASSERT(AfxIsValidAddress(pNode, sizeof(CNode)));
  1055.             SerializeElements<TYPE>(ar, &pNode->data, 1);
  1056.         }
  1057.     }
  1058.     else
  1059.     {
  1060.         DWORD nNewCount = ar.ReadCount();
  1061.         while (nNewCount--)
  1062.         {
  1063.             TYPE newData;
  1064.             SerializeElements<TYPE>(ar, &newData, 1);
  1065.             AddTail(newData);
  1066.         }
  1067.     }
  1068. }
  1069.  
  1070. #ifdef _DEBUG
  1071. template<class TYPE, class ARG_TYPE>
  1072. void CList<TYPE, ARG_TYPE>::Dump(CDumpContext& dc) const
  1073. {
  1074.     CObject::Dump(dc);
  1075.  
  1076.     dc << "with " << m_nCount << " elements";
  1077.     if (dc.GetDepth() > 0)
  1078.     {
  1079.         POSITION pos = GetHeadPosition();
  1080.         while (pos != NULL)
  1081.         {
  1082.             dc << "\n";
  1083.             DumpElements<TYPE>(dc, &((CList*)this)->GetNext(pos), 1);
  1084.         }
  1085.     }
  1086.  
  1087.     dc << "\n";
  1088. }
  1089.  
  1090. template<class TYPE, class ARG_TYPE>
  1091. void CList<TYPE, ARG_TYPE>::AssertValid() const
  1092. {
  1093.     CObject::AssertValid();
  1094.  
  1095.     if (m_nCount == 0)
  1096.     {
  1097.         // empty list
  1098.         ASSERT(m_pNodeHead == NULL);
  1099.         ASSERT(m_pNodeTail == NULL);
  1100.     }
  1101.     else
  1102.     {
  1103.         // non-empty list
  1104.         ASSERT(AfxIsValidAddress(m_pNodeHead, sizeof(CNode)));
  1105.         ASSERT(AfxIsValidAddress(m_pNodeTail, sizeof(CNode)));
  1106.     }
  1107. }
  1108. #endif //_DEBUG
  1109.  
  1110. /////////////////////////////////////////////////////////////////////////////
  1111. // CMap<KEY, ARG_KEY, VALUE, ARG_VALUE>
  1112.  
  1113. template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
  1114. class CMap : public CObject
  1115. {
  1116. protected:
  1117.     // Association
  1118.     struct CAssoc
  1119.     {
  1120.         CAssoc* pNext;
  1121.         UINT nHashValue;  // needed for efficient iteration
  1122.         KEY key;
  1123.         VALUE value;
  1124.     };
  1125. public:
  1126. // Construction
  1127.     CMap(int nBlockSize = 10);
  1128.  
  1129. // Attributes
  1130.     // number of elements
  1131.     int GetCount() const;
  1132.     BOOL IsEmpty() const;
  1133.  
  1134.     // Lookup
  1135.     BOOL Lookup(ARG_KEY key, VALUE& rValue) const;
  1136.  
  1137. // Operations
  1138.     // Lookup and add if not there
  1139.     VALUE& operator[](ARG_KEY key);
  1140.  
  1141.     // add a new (key, value) pair
  1142.     void SetAt(ARG_KEY key, ARG_VALUE newValue);
  1143.  
  1144.     // removing existing (key, ?) pair
  1145.     BOOL RemoveKey(ARG_KEY key);
  1146.     void RemoveAll();
  1147.  
  1148.     // iterating all (key, value) pairs
  1149.     POSITION GetStartPosition() const;
  1150.     void GetNextAssoc(POSITION& rNextPosition, KEY& rKey, VALUE& rValue) const;
  1151.  
  1152.     // advanced features for derived classes
  1153.     UINT GetHashTableSize() const;
  1154.     void InitHashTable(UINT hashSize, BOOL bAllocNow = TRUE);
  1155.  
  1156. // Implementation
  1157. protected:
  1158.     CAssoc** m_pHashTable;
  1159.     UINT m_nHashTableSize;
  1160.     int m_nCount;
  1161.     CAssoc* m_pFreeList;
  1162.     struct CPlex* m_pBlocks;
  1163.     int m_nBlockSize;
  1164.  
  1165.     CAssoc* NewAssoc();
  1166.     void FreeAssoc(CAssoc*);
  1167.     CAssoc* GetAssocAt(ARG_KEY, UINT&) const;
  1168.  
  1169. public:
  1170.     ~CMap();
  1171.     void Serialize(CArchive&);
  1172. #ifdef _DEBUG
  1173.     void Dump(CDumpContext&) const;
  1174.     void AssertValid() const;
  1175. #endif
  1176. };
  1177.  
  1178. /////////////////////////////////////////////////////////////////////////////
  1179. // CMap<KEY, ARG_KEY, VALUE, ARG_VALUE> inline functions
  1180.  
  1181. template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
  1182. inline int CMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::GetCount() const
  1183.     { return m_nCount; }
  1184. template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
  1185. inline BOOL CMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::IsEmpty() const
  1186.     { return m_nCount == 0; }
  1187. template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
  1188. inline void CMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::SetAt(ARG_KEY key, ARG_VALUE newValue)
  1189.     { (*this)[key] = newValue; }
  1190. template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
  1191. inline POSITION CMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::GetStartPosition() const
  1192.     { return (m_nCount == 0) ? NULL : BEFORE_START_POSITION; }
  1193. template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
  1194. inline UINT CMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::GetHashTableSize() const
  1195.     { return m_nHashTableSize; }
  1196.  
  1197. /////////////////////////////////////////////////////////////////////////////
  1198. // CMap<KEY, ARG_KEY, VALUE, ARG_VALUE> out-of-line functions
  1199.  
  1200. template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
  1201. CMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::CMap(int nBlockSize)
  1202. {
  1203.     ASSERT(nBlockSize > 0);
  1204.  
  1205.     m_pHashTable = NULL;
  1206.     m_nHashTableSize = 17;  // default size
  1207.     m_nCount = 0;
  1208.     m_pFreeList = NULL;
  1209.     m_pBlocks = NULL;
  1210.     m_nBlockSize = nBlockSize;
  1211. }
  1212.  
  1213. template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
  1214. void CMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::InitHashTable(
  1215.     UINT nHashSize, BOOL bAllocNow)
  1216. //
  1217. // Used to force allocation of a hash table or to override the default
  1218. //   hash table size of (which is fairly small)
  1219. {
  1220.     ASSERT_VALID(this);
  1221.     ASSERT(m_nCount == 0);
  1222.     ASSERT(nHashSize > 0);
  1223.  
  1224.     if (m_pHashTable != NULL)
  1225.     {
  1226.         // free hash table
  1227.         delete[] m_pHashTable;
  1228.         m_pHashTable = NULL;
  1229.     }
  1230.  
  1231.     if (bAllocNow)
  1232.     {
  1233.         m_pHashTable = new CAssoc* [nHashSize];
  1234.         memset(m_pHashTable, 0, sizeof(CAssoc*) * nHashSize);
  1235.     }
  1236.     m_nHashTableSize = nHashSize;
  1237. }
  1238.  
  1239. template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
  1240. void CMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::RemoveAll()
  1241. {
  1242.     ASSERT_VALID(this);
  1243.  
  1244.     if (m_pHashTable != NULL)
  1245.     {
  1246.         // destroy elements (values and keys)
  1247.         for (UINT nHash = 0; nHash < m_nHashTableSize; nHash++)
  1248.         {
  1249.             CAssoc* pAssoc;
  1250.             for (pAssoc = m_pHashTable[nHash]; pAssoc != NULL;
  1251.               pAssoc = pAssoc->pNext)
  1252.             {
  1253.                 DestructElements<VALUE>(&pAssoc->value, 1);
  1254.                 DestructElements<KEY>(&pAssoc->key, 1);
  1255.             }
  1256.         }
  1257.     }
  1258.  
  1259.     // free hash table
  1260.     delete[] m_pHashTable;
  1261.     m_pHashTable = NULL;
  1262.  
  1263.     m_nCount = 0;
  1264.     m_pFreeList = NULL;
  1265.     m_pBlocks->FreeDataChain();
  1266.     m_pBlocks = NULL;
  1267. }
  1268.  
  1269. template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
  1270. CMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::~CMap()
  1271. {
  1272.     RemoveAll();
  1273.     ASSERT(m_nCount == 0);
  1274. }
  1275.  
  1276. template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
  1277. CMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::CAssoc*
  1278. CMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::NewAssoc()
  1279. {
  1280.     if (m_pFreeList == NULL)
  1281.     {
  1282.         // add another block
  1283.         CPlex* newBlock = CPlex::Create(m_pBlocks, m_nBlockSize, sizeof(CMap::CAssoc));
  1284.         // chain them into free list
  1285.         CMap::CAssoc* pAssoc = (CMap::CAssoc*) newBlock->data();
  1286.         // free in reverse order to make it easier to debug
  1287.         pAssoc += m_nBlockSize - 1;
  1288.         for (int i = m_nBlockSize-1; i >= 0; i--, pAssoc--)
  1289.         {
  1290.             pAssoc->pNext = m_pFreeList;
  1291.             m_pFreeList = pAssoc;
  1292.         }
  1293.     }
  1294.     ASSERT(m_pFreeList != NULL);  // we must have something
  1295.  
  1296.     CMap::CAssoc* pAssoc = m_pFreeList;
  1297.     m_pFreeList = m_pFreeList->pNext;
  1298.     m_nCount++;
  1299.     ASSERT(m_nCount > 0);  // make sure we don't overflow
  1300.     ConstructElements<KEY>(&pAssoc->key, 1);
  1301.     ConstructElements<VALUE>(&pAssoc->value, 1);   // special construct values
  1302.     return pAssoc;
  1303. }
  1304.  
  1305. template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
  1306. void CMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::FreeAssoc(CMap::CAssoc* pAssoc)
  1307. {
  1308.     DestructElements<VALUE>(&pAssoc->value, 1);
  1309.     DestructElements<KEY>(&pAssoc->key, 1);
  1310.     pAssoc->pNext = m_pFreeList;
  1311.     m_pFreeList = pAssoc;
  1312.     m_nCount--;
  1313.     ASSERT(m_nCount >= 0);  // make sure we don't underflow
  1314.  
  1315.     // if no more elements, cleanup completely
  1316.     if (m_nCount == 0)
  1317.         RemoveAll();
  1318. }
  1319.  
  1320. template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
  1321. CMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::CAssoc*
  1322. CMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::GetAssocAt(ARG_KEY key, UINT& nHash) const
  1323. // find association (or return NULL)
  1324. {
  1325.     nHash = HashKey<ARG_KEY>(key) % m_nHashTableSize;
  1326.  
  1327.     if (m_pHashTable == NULL)
  1328.         return NULL;
  1329.  
  1330.     // see if it exists
  1331.     CAssoc* pAssoc;
  1332.     for (pAssoc = m_pHashTable[nHash]; pAssoc != NULL; pAssoc = pAssoc->pNext)
  1333.     {
  1334.         if (CompareElements(&pAssoc->key, &key))
  1335.             return pAssoc;
  1336.     }
  1337.     return NULL;
  1338. }
  1339.  
  1340. template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
  1341. BOOL CMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::Lookup(ARG_KEY key, VALUE& rValue) const
  1342. {
  1343.     ASSERT_VALID(this);
  1344.  
  1345.     UINT nHash;
  1346.     CAssoc* pAssoc = GetAssocAt(key, nHash);
  1347.     if (pAssoc == NULL)
  1348.         return FALSE;  // not in map
  1349.  
  1350.     rValue = pAssoc->value;
  1351.     return TRUE;
  1352. }
  1353.  
  1354. template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
  1355. VALUE& CMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::operator[](ARG_KEY key)
  1356. {
  1357.     ASSERT_VALID(this);
  1358.  
  1359.     UINT nHash;
  1360.     CAssoc* pAssoc;
  1361.     if ((pAssoc = GetAssocAt(key, nHash)) == NULL)
  1362.     {
  1363.         if (m_pHashTable == NULL)
  1364.             InitHashTable(m_nHashTableSize);
  1365.  
  1366.         // it doesn't exist, add a new Association
  1367.         pAssoc = NewAssoc();
  1368.         pAssoc->nHashValue = nHash;
  1369.         pAssoc->key = key;
  1370.         // 'pAssoc->value' is a constructed object, nothing more
  1371.  
  1372.         // put into hash table
  1373.         pAssoc->pNext = m_pHashTable[nHash];
  1374.         m_pHashTable[nHash] = pAssoc;
  1375.     }
  1376.     return pAssoc->value;  // return new reference
  1377. }
  1378.  
  1379. template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
  1380. BOOL CMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::RemoveKey(ARG_KEY key)
  1381. // remove key - return TRUE if removed
  1382. {
  1383.     ASSERT_VALID(this);
  1384.  
  1385.     if (m_pHashTable == NULL)
  1386.         return FALSE;  // nothing in the table
  1387.  
  1388.     CAssoc** ppAssocPrev;
  1389.     ppAssocPrev = &m_pHashTable[HashKey<ARG_KEY>(key) % m_nHashTableSize];
  1390.  
  1391.     CAssoc* pAssoc;
  1392.     for (pAssoc = *ppAssocPrev; pAssoc != NULL; pAssoc = pAssoc->pNext)
  1393.     {
  1394.         if (CompareElements(&pAssoc->key, &key))
  1395.         {
  1396.             // remove it
  1397.             *ppAssocPrev = pAssoc->pNext;  // remove from list
  1398.             FreeAssoc(pAssoc);
  1399.             return TRUE;
  1400.         }
  1401.         ppAssocPrev = &pAssoc->pNext;
  1402.     }
  1403.     return FALSE;  // not found
  1404. }
  1405.  
  1406. template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
  1407. void CMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::GetNextAssoc(POSITION& rNextPosition,
  1408.     KEY& rKey, VALUE& rValue) const
  1409. {
  1410.     ASSERT_VALID(this);
  1411.     ASSERT(m_pHashTable != NULL);  // never call on empty map
  1412.  
  1413.     CAssoc* pAssocRet = (CAssoc*)rNextPosition;
  1414.     ASSERT(pAssocRet != NULL);
  1415.  
  1416.     if (pAssocRet == (CAssoc*) BEFORE_START_POSITION)
  1417.     {
  1418.         // find the first association
  1419.         for (UINT nBucket = 0; nBucket < m_nHashTableSize; nBucket++)
  1420.             if ((pAssocRet = m_pHashTable[nBucket]) != NULL)
  1421.                 break;
  1422.         ASSERT(pAssocRet != NULL);  // must find something
  1423.     }
  1424.  
  1425.     // find next association
  1426.     ASSERT(AfxIsValidAddress(pAssocRet, sizeof(CAssoc)));
  1427.     CAssoc* pAssocNext;
  1428.     if ((pAssocNext = pAssocRet->pNext) == NULL)
  1429.     {
  1430.         // go to next bucket
  1431.         for (UINT nBucket = pAssocRet->nHashValue + 1;
  1432.           nBucket < m_nHashTableSize; nBucket++)
  1433.             if ((pAssocNext = m_pHashTable[nBucket]) != NULL)
  1434.                 break;
  1435.     }
  1436.  
  1437.     rNextPosition = (POSITION) pAssocNext;
  1438.  
  1439.     // fill in return data
  1440.     rKey = pAssocRet->key;
  1441.     rValue = pAssocRet->value;
  1442. }
  1443.  
  1444. template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
  1445. void CMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::Serialize(CArchive& ar)
  1446. {
  1447.     ASSERT_VALID(this);
  1448.  
  1449.     CObject::Serialize(ar);
  1450.  
  1451.     if (ar.IsStoring())
  1452.     {
  1453.         ar.WriteCount(m_nCount);
  1454.         if (m_nCount == 0)
  1455.             return;  // nothing more to do
  1456.  
  1457.         ASSERT(m_pHashTable != NULL);
  1458.         for (UINT nHash = 0; nHash < m_nHashTableSize; nHash++)
  1459.         {
  1460.             CAssoc* pAssoc;
  1461.             for (pAssoc = m_pHashTable[nHash]; pAssoc != NULL;
  1462.               pAssoc = pAssoc->pNext)
  1463.             {
  1464.                 SerializeElements<KEY>(ar, &pAssoc->key, 1);
  1465.                 SerializeElements<VALUE>(ar, &pAssoc->value, 1);
  1466.             }
  1467.         }
  1468.     }
  1469.     else
  1470.     {
  1471.         DWORD nNewCount = ar.ReadCount();
  1472.         while (nNewCount--)
  1473.         {
  1474.             KEY newKey;
  1475.             VALUE newValue;
  1476.             SerializeElements<KEY>(ar, &newKey, 1);
  1477.             SerializeElements<VALUE>(ar, &newValue, 1);
  1478.             SetAt(newKey, newValue);
  1479.         }
  1480.     }
  1481. }
  1482.  
  1483. #ifdef _DEBUG
  1484. template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
  1485. void CMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::Dump(CDumpContext& dc) const
  1486. {
  1487.     CObject::Dump(dc);
  1488.  
  1489.     dc << "with " << m_nCount << " elements";
  1490.     if (dc.GetDepth() > 0)
  1491.     {
  1492.         // Dump in format "[key] -> value"
  1493.         KEY key;
  1494.         VALUE val;
  1495.  
  1496.         POSITION pos = GetStartPosition();
  1497.         while (pos != NULL)
  1498.         {
  1499.             GetNextAssoc(pos, key, val);
  1500.             dc << "\n\t[";
  1501.             DumpElements<KEY>(dc, &key, 1);
  1502.             dc << "] = ";
  1503.             DumpElements<VALUE>(dc, &val, 1);
  1504.         }
  1505.     }
  1506.  
  1507.     dc << "\n";
  1508. }
  1509.  
  1510. template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
  1511. void CMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::AssertValid() const
  1512. {
  1513.     CObject::AssertValid();
  1514.  
  1515.     ASSERT(m_nHashTableSize > 0);
  1516.     ASSERT(m_nCount == 0 || m_pHashTable != NULL);
  1517.         // non-empty map should have hash table
  1518. }
  1519. #endif //_DEBUG
  1520.  
  1521. /////////////////////////////////////////////////////////////////////////////
  1522. // CTypedPtrArray<BASE_CLASS, TYPE>
  1523.  
  1524. template<class BASE_CLASS, class TYPE>
  1525. class CTypedPtrArray : public BASE_CLASS
  1526. {
  1527. public:
  1528.     // Accessing elements
  1529.     TYPE GetAt(int nIndex) const
  1530.         { return (TYPE)BASE_CLASS::GetAt(nIndex); }
  1531.     TYPE& ElementAt(int nIndex)
  1532.         { return (TYPE&)BASE_CLASS::ElementAt(nIndex); }
  1533.     void SetAt(int nIndex, TYPE ptr)
  1534.         { BASE_CLASS::SetAt(nIndex, ptr); }
  1535.  
  1536.     // Potentially growing the array
  1537.     void SetAtGrow(int nIndex, TYPE newElement)
  1538.        { BASE_CLASS::SetAtGrow(nIndex, newElement); }
  1539.     int Add(TYPE newElement)
  1540.        { return BASE_CLASS::Add(newElement); }
  1541.     int Append(const CTypedPtrArray<BASE_CLASS, TYPE>& src)
  1542.        { return BASE_CLASS::Append(src); }
  1543.     void Copy(const CTypedPtrArray<BASE_CLASS, TYPE>& src)
  1544.         { BASE_CLASS::Copy(src); }
  1545.  
  1546.     // Operations that move elements around
  1547.     void InsertAt(int nIndex, TYPE newElement, int nCount = 1)
  1548.         { BASE_CLASS::InsertAt(nIndex, newElement, nCount); }
  1549.     void InsertAt(int nStartIndex, CTypedPtrArray<BASE_CLASS, TYPE>* pNewArray)
  1550.        { BASE_CLASS::InsertAt(nStartIndex, pNewArray); }
  1551.  
  1552.     // overloaded operator helpers
  1553.     TYPE operator[](int nIndex) const
  1554.         { return (TYPE)BASE_CLASS::operator[](nIndex); }
  1555.     TYPE& operator[](int nIndex)
  1556.         { return (TYPE&)BASE_CLASS::operator[](nIndex); }
  1557. };
  1558.  
  1559. /////////////////////////////////////////////////////////////////////////////
  1560. // CTypedPtrList<BASE_CLASS, TYPE>
  1561.  
  1562. template<class BASE_CLASS, class TYPE>
  1563. class CTypedPtrList : public BASE_CLASS
  1564. {
  1565. public:
  1566. // Construction
  1567.     CTypedPtrList(int nBlockSize = 10)
  1568.         : BASE_CLASS(nBlockSize) { }
  1569.  
  1570.     // peek at head or tail
  1571.     TYPE& GetHead()
  1572.         { return (TYPE&)BASE_CLASS::GetHead(); }
  1573.     TYPE GetHead() const
  1574.         { return (TYPE)BASE_CLASS::GetHead(); }
  1575.     TYPE& GetTail()
  1576.         { return (TYPE&)BASE_CLASS::GetTail(); }
  1577.     TYPE GetTail() const
  1578.         { return (TYPE)BASE_CLASS::GetTail(); }
  1579.  
  1580.     // get head or tail (and remove it) - don't call on empty list!
  1581.     TYPE RemoveHead()
  1582.         { return (TYPE)BASE_CLASS::RemoveHead(); }
  1583.     TYPE RemoveTail()
  1584.         { return (TYPE)BASE_CLASS::RemoveTail(); }
  1585.  
  1586.     // add before head or after tail
  1587.     POSITION AddHead(TYPE newElement)
  1588.         { return BASE_CLASS::AddHead(newElement); }
  1589.     POSITION AddTail(TYPE newElement)
  1590.         { return BASE_CLASS::AddTail(newElement); }
  1591.  
  1592.     // add another list of elements before head or after tail
  1593.     void AddHead(CTypedPtrList<BASE_CLASS, TYPE>* pNewList)
  1594.         { BASE_CLASS::AddHead(pNewList); }
  1595.     void AddTail(CTypedPtrList<BASE_CLASS, TYPE>* pNewList)
  1596.         { BASE_CLASS::AddTail(pNewList); }
  1597.  
  1598.     // iteration
  1599.     TYPE& GetNext(POSITION& rPosition)
  1600.         { return (TYPE&)BASE_CLASS::GetNext(rPosition); }
  1601.     TYPE GetNext(POSITION& rPosition) const
  1602.         { return (TYPE)BASE_CLASS::GetNext(rPosition); }
  1603.     TYPE& GetPrev(POSITION& rPosition)
  1604.         { return (TYPE&)BASE_CLASS::GetPrev(rPosition); }
  1605.     TYPE GetPrev(POSITION& rPosition) const
  1606.         { return (TYPE)BASE_CLASS::GetPrev(rPosition); }
  1607.  
  1608.     // getting/modifying an element at a given position
  1609.     TYPE& GetAt(POSITION position)
  1610.         { return (TYPE&)BASE_CLASS::GetAt(position); }
  1611.     TYPE GetAt(POSITION position) const
  1612.         { return (TYPE)BASE_CLASS::GetAt(position); }
  1613.     void SetAt(POSITION pos, TYPE newElement)
  1614.         { BASE_CLASS::SetAt(pos, newElement); }
  1615. };
  1616.  
  1617. /////////////////////////////////////////////////////////////////////////////
  1618. // CTypedPtrMap<BASE_CLASS, KEY, VALUE>
  1619.  
  1620. template<class BASE_CLASS, class KEY, class VALUE>
  1621. class CTypedPtrMap : public BASE_CLASS
  1622. {
  1623. public:
  1624.  
  1625. // Construction
  1626.     CTypedPtrMap(int nBlockSize = 10)
  1627.         : BASE_CLASS(nBlockSize) { }
  1628.  
  1629.     // Lookup
  1630.     BOOL Lookup(BASE_CLASS::BASE_ARG_KEY key, VALUE& rValue) const
  1631.         { return BASE_CLASS::Lookup(key, (BASE_CLASS::BASE_VALUE&)rValue); }
  1632.  
  1633.     // Lookup and add if not there
  1634.     VALUE& operator[](BASE_CLASS::BASE_ARG_KEY key)
  1635.         { return (VALUE&)BASE_CLASS::operator[](key); }
  1636.  
  1637.     // add a new key (key, value) pair
  1638.     void SetAt(KEY key, VALUE newValue)
  1639.         { BASE_CLASS::SetAt(key, newValue); }
  1640.  
  1641.     // removing existing (key, ?) pair
  1642.     BOOL RemoveKey(KEY key)
  1643.         { return BASE_CLASS::RemoveKey(key); }
  1644.  
  1645.     // iteration
  1646.     void GetNextAssoc(POSITION& rPosition, KEY& rKey, VALUE& rValue) const
  1647.         { BASE_CLASS::GetNextAssoc(rPosition, (BASE_CLASS::BASE_KEY&)rKey,
  1648.             (BASE_CLASS::BASE_VALUE&)rValue); }
  1649. };
  1650.  
  1651. /////////////////////////////////////////////////////////////////////////////
  1652.  
  1653. #undef THIS_FILE
  1654. #define THIS_FILE __FILE__
  1655.  
  1656. #undef new
  1657. #ifdef _REDEF_NEW
  1658. #define new DEBUG_NEW
  1659. #undef _REDEF_NEW
  1660. #endif
  1661.  
  1662. #ifdef _AFX_PACKING
  1663. #pragma pack(pop)
  1664. #endif
  1665.  
  1666. #ifdef _AFX_MINREBUILD
  1667. #pragma component(minrebuild, on)
  1668. #endif
  1669. #ifndef _AFX_FULLTYPEINFO
  1670. #pragma component(mintypeinfo, off)
  1671. #endif
  1672.  
  1673. #endif //__AFXTEMPL_H__
  1674.  
  1675. /////////////////////////////////////////////////////////////////////////////
  1676.