// This is a part of the Microsoft Foundation Classes C++ library. // Copyright (C) Microsoft Corporation // All rights reserved. // // This source code is only intended as a supplement to the // Microsoft Foundation Classes Reference and related // electronic documentation provided with the library. // See these sources for detailed information regarding the // Microsoft Foundation Classes product. #ifndef __AFXTEMPL_H__ #define __AFXTEMPL_H__ #ifndef __AFXPLEX_H__ #include #endif #ifdef _AFX_MINREBUILD #pragma component(minrebuild, off) #endif #ifndef _AFX_FULLTYPEINFO #pragma component(mintypeinfo, on) #endif #ifdef _AFX_PACKING #pragma pack(push, _AFX_PACKING) #endif #ifdef _DEBUG #endif #pragma warning( push ) #if _SECURE_ATL #pragma warning( disable: 4505 4127 ) #endif ///////////////////////////////////////////////////////////////////////////// // global helpers (can be overridden) #pragma push_macro("new") #ifndef _INC_NEW #include #endif namespace ATL { class CComBSTR; } using ATL::CComBSTR; // the two functions below are deprecated. Use a constructor/destructor instead. #pragma deprecated( DestructElements ) #pragma deprecated( ConstructElements ) template AFX_INLINE void AFXAPI CopyElements(TYPE* pDest, const TYPE* pSrc, INT_PTR nCount) { ENSURE(nCount == 0 || pDest != 0 && pSrc != 0); ASSERT(nCount == 0 || AfxIsValidAddress(pDest, (size_t)nCount * sizeof(TYPE))); ASSERT(nCount == 0 || AfxIsValidAddress(pSrc, (size_t)nCount * sizeof(TYPE))); // default is element-copy using assignment while (nCount--) *pDest++ = *pSrc++; } template void AFXAPI SerializeElements(CArchive& ar, TYPE* pElements, INT_PTR nCount) { ENSURE(nCount == 0 || pElements != NULL); ASSERT(nCount == 0 || AfxIsValidAddress(pElements, (size_t)nCount * sizeof(TYPE))); // default is bit-wise read/write if (ar.IsStoring()) { TYPE* pData; UINT_PTR nElementsLeft; nElementsLeft = nCount; pData = pElements; while( nElementsLeft > 0 ) { UINT nElementsToWrite; nElementsToWrite = UINT(__min(nElementsLeft, INT_MAX/sizeof(TYPE))); ar.Write(pData, nElementsToWrite*sizeof(TYPE)); nElementsLeft -= nElementsToWrite; pData += nElementsToWrite; } } else { TYPE* pData; UINT_PTR nElementsLeft; nElementsLeft = nCount; pData = pElements; while( nElementsLeft > 0 ) { UINT nElementsToRead; nElementsToRead = UINT(__min(nElementsLeft, INT_MAX/sizeof(TYPE))); ar.EnsureRead(pData, nElementsToRead*sizeof(TYPE)); nElementsLeft -= nElementsToRead; pData += nElementsToRead; } } } template void AFXAPI SerializeElementsInsertExtract(CArchive& ar, TYPE* pElements, INT_PTR nCount) { ENSURE(nCount == 0 || pElements != NULL); ASSERT((nCount == 0) || (AfxIsValidAddress(pElements, nCount*sizeof(TYPE)))); if (nCount == 0 || pElements == NULL) { return; } if (ar.IsStoring()) { for (; nCount--; ++pElements) ar << *pElements; } else { for (; nCount--; ++pElements) ar >> *pElements; } } #ifdef _DEBUG template void AFXAPI DumpElements(CDumpContext& dc, const TYPE* pElements, INT_PTR nCount) { ENSURE(nCount == 0 || pElements != NULL); ASSERT(nCount == 0 || AfxIsValidAddress(pElements, (size_t)nCount * sizeof(TYPE), FALSE)); &dc; // not used pElements; // not used nCount; // not used // default does nothing } #endif template BOOL AFXAPI CompareElements(const TYPE* pElement1, const ARG_TYPE* pElement2) { ENSURE(pElement1 != NULL && pElement2 != NULL); ASSERT(AfxIsValidAddress(pElement1, sizeof(TYPE), FALSE)); ASSERT(AfxIsValidAddress(pElement2, sizeof(ARG_TYPE), FALSE)); return *pElement1 == *pElement2; } template AFX_INLINE UINT AFXAPI HashKey(ARG_KEY key) { // default identity hash - works for most primitive values return (DWORD)(((DWORD_PTR)key)>>4); } // special versions for CString #if _MSC_VER >= 1100 template<> void AFXAPI SerializeElements (CArchive& ar, CStringA* pElements, INT_PTR nCount); template<> void AFXAPI SerializeElements (CArchive& ar, CStringW* pElements, INT_PTR nCount); template<> UINT AFXAPI HashKey (LPCWSTR key); template<> UINT AFXAPI HashKey (LPCSTR key); #else // _MSC_VER >= 1100 void AFXAPI SerializeElements(CArchive& ar, CString* pElements, INT_PTR nCount); UINT AFXAPI HashKey(LPCWSTR key); UINT AFXAPI HashKey(LPCSTR key); #endif // _MSC_VER >= 1100 // special versions for CComBSTR template<> void AFXAPI SerializeElements (CArchive& ar, CComBSTR* pElements, INT_PTR nCount); template<> UINT AFXAPI HashKey (CComBSTR key); // forward declarations class COleVariant; struct tagVARIANT; // special versions for COleVariant #if _MSC_VER >= 1100 template<> void AFXAPI CopyElements (COleVariant* pDest, const COleVariant* pSrc, INT_PTR nCount); template<> void AFXAPI SerializeElements (CArchive& ar, COleVariant* pElements, INT_PTR nCount); #ifdef _DEBUG template<> void AFXAPI DumpElements (CDumpContext& dc, const COleVariant* pElements, INT_PTR nCount); #endif template<> UINT AFXAPI HashKey (const struct tagVARIANT& var); #else // _MSC_VER >= 1100 void AFXAPI CopyElements(COleVariant* pDest, const COleVariant* pSrc, INT_PTR nCount); void AFXAPI SerializeElements(CArchive& ar, COleVariant* pElements, INT_PTR nCount); #ifdef _DEBUG void AFXAPI DumpElements(CDumpContext& dc, const COleVariant* pElements, INT_PTR nCount); #endif UINT AFXAPI HashKey(const struct tagVARIANT& var); #endif // _MSC_VER >= 1100 #define new DEBUG_NEW ///////////////////////////////////////////////////////////////////////////// // CArray template class CArray : public CObject { public: // Construction CArray(); // Attributes INT_PTR GetSize() const; INT_PTR GetCount() const; BOOL IsEmpty() const; INT_PTR GetUpperBound() const; void SetSize(INT_PTR nNewSize, INT_PTR nGrowBy = -1); // Operations // Clean up void FreeExtra(); void RemoveAll(); // Accessing elements const TYPE& GetAt(INT_PTR nIndex) const; TYPE& GetAt(INT_PTR nIndex); void SetAt(INT_PTR nIndex, ARG_TYPE newElement); const TYPE& ElementAt(INT_PTR nIndex) const; TYPE& ElementAt(INT_PTR nIndex); // Direct Access to the element data (may return NULL) const TYPE* GetData() const; TYPE* GetData(); // Potentially growing the array void SetAtGrow(INT_PTR nIndex, ARG_TYPE newElement); INT_PTR Add(ARG_TYPE newElement); INT_PTR Append(const CArray& src); void Copy(const CArray& src); // overloaded operator helpers const TYPE& operator[](INT_PTR nIndex) const; TYPE& operator[](INT_PTR nIndex); // Operations that move elements around void InsertAt(INT_PTR nIndex, ARG_TYPE newElement, INT_PTR nCount = 1); void RemoveAt(INT_PTR nIndex, INT_PTR nCount = 1); void InsertAt(INT_PTR nStartIndex, CArray* pNewArray); // Implementation protected: TYPE* m_pData; // the actual array of data INT_PTR m_nSize; // # of elements (upperBound - 1) INT_PTR m_nMaxSize; // max allocated INT_PTR m_nGrowBy; // grow amount public: ~CArray(); void Serialize(CArchive&); #ifdef _DEBUG void Dump(CDumpContext&) const; void AssertValid() const; #endif }; ///////////////////////////////////////////////////////////////////////////// // CArray inline functions template AFX_INLINE INT_PTR CArray::GetSize() const { return m_nSize; } template AFX_INLINE INT_PTR CArray::GetCount() const { return m_nSize; } template AFX_INLINE BOOL CArray::IsEmpty() const { return m_nSize == 0; } template AFX_INLINE INT_PTR CArray::GetUpperBound() const { return m_nSize-1; } template AFX_INLINE void CArray::RemoveAll() { SetSize(0, -1); } template AFX_INLINE TYPE& CArray::GetAt(INT_PTR nIndex) { ASSERT(nIndex >= 0 && nIndex < m_nSize); if(nIndex >= 0 && nIndex < m_nSize) return m_pData[nIndex]; AfxThrowInvalidArgException(); } template AFX_INLINE const TYPE& CArray::GetAt(INT_PTR nIndex) const { ASSERT(nIndex >= 0 && nIndex < m_nSize); if(nIndex >= 0 && nIndex < m_nSize) return m_pData[nIndex]; AfxThrowInvalidArgException(); } template AFX_INLINE void CArray::SetAt(INT_PTR nIndex, ARG_TYPE newElement) { ASSERT(nIndex >= 0 && nIndex < m_nSize); if(nIndex >= 0 && nIndex < m_nSize) m_pData[nIndex] = newElement; else AfxThrowInvalidArgException(); } template AFX_INLINE const TYPE& CArray::ElementAt(INT_PTR nIndex) const { ASSERT(nIndex >= 0 && nIndex < m_nSize); if(nIndex >= 0 && nIndex < m_nSize) return m_pData[nIndex]; AfxThrowInvalidArgException(); } template AFX_INLINE TYPE& CArray::ElementAt(INT_PTR nIndex) { ASSERT(nIndex >= 0 && nIndex < m_nSize); if(nIndex >= 0 && nIndex < m_nSize) return m_pData[nIndex]; AfxThrowInvalidArgException(); } template AFX_INLINE const TYPE* CArray::GetData() const { return (const TYPE*)m_pData; } template AFX_INLINE TYPE* CArray::GetData() { return (TYPE*)m_pData; } template AFX_INLINE INT_PTR CArray::Add(ARG_TYPE newElement) { INT_PTR nIndex = m_nSize; SetAtGrow(nIndex, newElement); return nIndex; } template AFX_INLINE const TYPE& CArray::operator[](INT_PTR nIndex) const { return GetAt(nIndex); } template AFX_INLINE TYPE& CArray::operator[](INT_PTR nIndex) { return ElementAt(nIndex); } ///////////////////////////////////////////////////////////////////////////// // CArray out-of-line functions template CArray::CArray() { m_pData = NULL; m_nSize = m_nMaxSize = m_nGrowBy = 0; } template CArray::~CArray() { ASSERT_VALID(this); if (m_pData != NULL) { for( int i = 0; i < m_nSize; i++ ) (m_pData + i)->~TYPE(); delete[] (BYTE*)m_pData; } } template void CArray::SetSize(INT_PTR nNewSize, INT_PTR nGrowBy) { ASSERT_VALID(this); ASSERT(nNewSize >= 0); if(nNewSize < 0 ) AfxThrowInvalidArgException(); if (nGrowBy >= 0) m_nGrowBy = nGrowBy; // set new size if (nNewSize == 0) { // shrink to nothing if (m_pData != NULL) { for( int i = 0; i < m_nSize; i++ ) (m_pData + i)->~TYPE(); delete[] (BYTE*)m_pData; m_pData = NULL; } m_nSize = m_nMaxSize = 0; } else if (m_pData == NULL) { // create buffer big enough to hold number of requested elements or // m_nGrowBy elements, whichever is larger. #ifdef SIZE_T_MAX ASSERT(nNewSize <= SIZE_T_MAX/sizeof(TYPE)); // no overflow #endif size_t nAllocSize = __max(nNewSize, m_nGrowBy); m_pData = (TYPE*) new BYTE[(size_t)nAllocSize * sizeof(TYPE)]; memset((void*)m_pData, 0, (size_t)nAllocSize * sizeof(TYPE)); for( int i = 0; i < nNewSize; i++ ) #pragma push_macro("new") #undef new ::new( (void*)( m_pData + i ) ) TYPE; #pragma pop_macro("new") m_nSize = nNewSize; m_nMaxSize = nAllocSize; } else if (nNewSize <= m_nMaxSize) { // it fits if (nNewSize > m_nSize) { // initialize the new elements memset((void*)(m_pData + m_nSize), 0, (size_t)(nNewSize-m_nSize) * sizeof(TYPE)); for( int i = 0; i < nNewSize-m_nSize; i++ ) #pragma push_macro("new") #undef new ::new( (void*)( m_pData + m_nSize + i ) ) TYPE; #pragma pop_macro("new") } else if (m_nSize > nNewSize) { // destroy the old elements for( int i = 0; i < m_nSize-nNewSize; i++ ) (m_pData + nNewSize + i)->~TYPE(); } m_nSize = nNewSize; } else { // otherwise, grow array nGrowBy = m_nGrowBy; if (nGrowBy == 0) { // heuristically determine growth when nGrowBy == 0 // (this avoids heap fragmentation in many situations) nGrowBy = m_nSize / 8; nGrowBy = (nGrowBy < 4) ? 4 : ((nGrowBy > 1024) ? 1024 : nGrowBy); } INT_PTR nNewMax; if (nNewSize < m_nMaxSize + nGrowBy) nNewMax = m_nMaxSize + nGrowBy; // granularity else nNewMax = nNewSize; // no slush ASSERT(nNewMax >= m_nMaxSize); // no wrap around if(nNewMax < m_nMaxSize) AfxThrowInvalidArgException(); #ifdef SIZE_T_MAX ASSERT(nNewMax <= SIZE_T_MAX/sizeof(TYPE)); // no overflow #endif TYPE* pNewData = (TYPE*) new BYTE[(size_t)nNewMax * sizeof(TYPE)]; // copy new data from old ::ATL::Checked::memcpy_s(pNewData, (size_t)nNewMax * sizeof(TYPE), m_pData, (size_t)m_nSize * sizeof(TYPE)); // construct remaining elements ASSERT(nNewSize > m_nSize); memset((void*)(pNewData + m_nSize), 0, (size_t)(nNewSize-m_nSize) * sizeof(TYPE)); for( int i = 0; i < nNewSize-m_nSize; i++ ) #pragma push_macro("new") #undef new ::new( (void*)( pNewData + m_nSize + i ) ) TYPE; #pragma pop_macro("new") // get rid of old stuff (note: no destructors called) delete[] (BYTE*)m_pData; m_pData = pNewData; m_nSize = nNewSize; m_nMaxSize = nNewMax; } } template INT_PTR CArray::Append(const CArray& src) { ASSERT_VALID(this); ASSERT(this != &src); // cannot append to itself if(this == &src) AfxThrowInvalidArgException(); INT_PTR nOldSize = m_nSize; SetSize(m_nSize + src.m_nSize); CopyElements(m_pData + nOldSize, src.m_pData, src.m_nSize); return nOldSize; } template void CArray::Copy(const CArray& src) { ASSERT_VALID(this); ASSERT(this != &src); // cannot append to itself if(this != &src) { SetSize(src.m_nSize); CopyElements(m_pData, src.m_pData, src.m_nSize); } } template void CArray::FreeExtra() { ASSERT_VALID(this); if (m_nSize != m_nMaxSize) { // shrink to desired size #ifdef SIZE_T_MAX ASSERT(m_nSize <= SIZE_T_MAX/sizeof(TYPE)); // no overflow #endif TYPE* pNewData = NULL; if (m_nSize != 0) { pNewData = (TYPE*) new BYTE[m_nSize * sizeof(TYPE)]; // copy new data from old ::ATL::Checked::memcpy_s(pNewData, m_nSize * sizeof(TYPE), m_pData, m_nSize * sizeof(TYPE)); } // get rid of old stuff (note: no destructors called) delete[] (BYTE*)m_pData; m_pData = pNewData; m_nMaxSize = m_nSize; } } template void CArray::SetAtGrow(INT_PTR nIndex, ARG_TYPE newElement) { ASSERT_VALID(this); ASSERT(nIndex >= 0); if(nIndex < 0) AfxThrowInvalidArgException(); if (nIndex >= m_nSize) SetSize(nIndex+1, -1); m_pData[nIndex] = newElement; } template void CArray::InsertAt(INT_PTR nIndex, ARG_TYPE newElement, INT_PTR nCount /*=1*/) { ASSERT_VALID(this); ASSERT(nIndex >= 0); // will expand to meet need ASSERT(nCount > 0); // zero or negative size not allowed if(nIndex < 0 || nCount <= 0) AfxThrowInvalidArgException(); if (nIndex >= m_nSize) { // adding after the end of the array SetSize(nIndex + nCount, -1); // grow so nIndex is valid } else { // inserting in the middle of the array INT_PTR nOldSize = m_nSize; SetSize(m_nSize + nCount, -1); // grow it to new size // destroy intial data before copying over it for( int i = 0; i < nCount; i++ ) (m_pData + nOldSize + i)->~TYPE(); // shift old data up to fill gap ::ATL::Checked::memmove_s(m_pData + nIndex + nCount, (nOldSize-nIndex) * sizeof(TYPE), m_pData + nIndex, (nOldSize-nIndex) * sizeof(TYPE)); // re-init slots we copied from memset((void*)(m_pData + nIndex), 0, (size_t)nCount * sizeof(TYPE)); for( int i = 0; i < nCount; i++ ) #pragma push_macro("new") #undef new ::new( (void*)( m_pData + nIndex + i ) ) TYPE; #pragma pop_macro("new") } // insert new value in the gap ASSERT(nIndex + nCount <= m_nSize); while (nCount--) m_pData[nIndex++] = newElement; } template void CArray::RemoveAt(INT_PTR nIndex, INT_PTR nCount) { ASSERT_VALID(this); ASSERT(nIndex >= 0); ASSERT(nCount >= 0); INT_PTR nUpperBound = nIndex + nCount; ASSERT(nUpperBound <= m_nSize && nUpperBound >= nIndex && nUpperBound >= nCount); if(nIndex < 0 || nCount < 0 || (nUpperBound > m_nSize) || (nUpperBound < nIndex) || (nUpperBound < nCount)) AfxThrowInvalidArgException(); // just remove a range INT_PTR nMoveCount = m_nSize - (nUpperBound); for( int i = 0; i < nCount; i++ ) (m_pData + nIndex + i)->~TYPE(); if (nMoveCount) { ::ATL::Checked::memmove_s(m_pData + nIndex, (size_t)nMoveCount * sizeof(TYPE), m_pData + nUpperBound, (size_t)nMoveCount * sizeof(TYPE)); } m_nSize -= nCount; } template void CArray::InsertAt(INT_PTR nStartIndex, CArray* pNewArray) { ASSERT_VALID(this); ASSERT(pNewArray != NULL); ASSERT_VALID(pNewArray); ASSERT(nStartIndex >= 0); if(pNewArray == NULL || nStartIndex < 0) AfxThrowInvalidArgException(); if (pNewArray->GetSize() > 0) { InsertAt(nStartIndex, pNewArray->GetAt(0), pNewArray->GetSize()); for (INT_PTR i = 0; i < pNewArray->GetSize(); i++) SetAt(nStartIndex + i, pNewArray->GetAt(i)); } } template void CArray::Serialize(CArchive& ar) { ASSERT_VALID(this); CObject::Serialize(ar); if (ar.IsStoring()) { ar.WriteCount(m_nSize); } else { DWORD_PTR nOldSize = ar.ReadCount(); SetSize(nOldSize, -1); } SerializeElements(ar, m_pData, m_nSize); } #ifdef _DEBUG template void CArray::Dump(CDumpContext& dc) const { CObject::Dump(dc); dc << "with " << m_nSize << " elements"; if (dc.GetDepth() > 0) { dc << "\n"; DumpElements(dc, m_pData, m_nSize); } dc << "\n"; } template void CArray::AssertValid() const { CObject::AssertValid(); if (m_pData == NULL) { ASSERT(m_nSize == 0); ASSERT(m_nMaxSize == 0); } else { ASSERT(m_nSize >= 0); ASSERT(m_nMaxSize >= 0); ASSERT(m_nSize <= m_nMaxSize); ASSERT(AfxIsValidAddress(m_pData, m_nMaxSize * sizeof(TYPE))); } } #endif //_DEBUG ///////////////////////////////////////////////////////////////////////////// // CList template class CList : public CObject { protected: struct CNode { CNode* pNext; CNode* pPrev; TYPE data; }; public: // Construction /* explicit */ CList(INT_PTR nBlockSize = 10); // Attributes (head and tail) // count of elements INT_PTR GetCount() const; INT_PTR GetSize() const; BOOL IsEmpty() const; // peek at head or tail TYPE& GetHead(); const TYPE& GetHead() const; TYPE& GetTail(); const TYPE& GetTail() const; // Operations // get head or tail (and remove it) - don't call on empty list ! TYPE RemoveHead(); TYPE RemoveTail(); // add before head or after tail POSITION AddHead(ARG_TYPE newElement); POSITION AddTail(ARG_TYPE newElement); // add another list of elements before head or after tail void AddHead(CList* pNewList); void AddTail(CList* pNewList); // remove all elements void RemoveAll(); // iteration POSITION GetHeadPosition() const; POSITION GetTailPosition() const; TYPE& GetNext(POSITION& rPosition); // return *Position++ const TYPE& GetNext(POSITION& rPosition) const; // return *Position++ TYPE& GetPrev(POSITION& rPosition); // return *Position-- const TYPE& GetPrev(POSITION& rPosition) const; // return *Position-- // getting/modifying an element at a given position TYPE& GetAt(POSITION position); const TYPE& GetAt(POSITION position) const; void SetAt(POSITION pos, ARG_TYPE newElement); void RemoveAt(POSITION position); // inserting before or after a given position POSITION InsertBefore(POSITION position, ARG_TYPE newElement); POSITION InsertAfter(POSITION position, ARG_TYPE newElement); // helper functions (note: O(n) speed) POSITION Find(ARG_TYPE searchValue, POSITION startAfter = NULL) const; // defaults to starting at the HEAD, return NULL if not found POSITION FindIndex(INT_PTR nIndex) const; // get the 'nIndex'th element (may return NULL) // Implementation protected: CNode* m_pNodeHead; CNode* m_pNodeTail; INT_PTR m_nCount; CNode* m_pNodeFree; struct CPlex* m_pBlocks; INT_PTR m_nBlockSize; CNode* NewNode(CNode*, CNode*); void FreeNode(CNode*); public: ~CList(); void Serialize(CArchive&); #ifdef _DEBUG void Dump(CDumpContext&) const; void AssertValid() const; #endif }; ///////////////////////////////////////////////////////////////////////////// // CList inline functions template AFX_INLINE INT_PTR CList::GetCount() const { return m_nCount; } template AFX_INLINE INT_PTR CList::GetSize() const { return m_nCount; } template AFX_INLINE BOOL CList::IsEmpty() const { return m_nCount == 0; } template AFX_INLINE TYPE& CList::GetHead() { ASSERT(m_pNodeHead != NULL); return m_pNodeHead->data; } template AFX_INLINE const TYPE& CList::GetHead() const { ASSERT(m_pNodeHead != NULL); return m_pNodeHead->data; } template AFX_INLINE TYPE& CList::GetTail() { ASSERT(m_pNodeTail != NULL); return m_pNodeTail->data; } template AFX_INLINE const TYPE& CList::GetTail() const { ASSERT(m_pNodeTail != NULL); return m_pNodeTail->data; } template AFX_INLINE POSITION CList::GetHeadPosition() const { return (POSITION) m_pNodeHead; } template AFX_INLINE POSITION CList::GetTailPosition() const { return (POSITION) m_pNodeTail; } template AFX_INLINE TYPE& CList::GetNext(POSITION& rPosition) // return *Position++ { CNode* pNode = (CNode*) rPosition; ASSERT(AfxIsValidAddress(pNode, sizeof(CNode))); rPosition = (POSITION) pNode->pNext; return pNode->data; } template AFX_INLINE const TYPE& CList::GetNext(POSITION& rPosition) const // return *Position++ { CNode* pNode = (CNode*) rPosition; ASSERT(AfxIsValidAddress(pNode, sizeof(CNode))); rPosition = (POSITION) pNode->pNext; return pNode->data; } template AFX_INLINE TYPE& CList::GetPrev(POSITION& rPosition) // return *Position-- { CNode* pNode = (CNode*) rPosition; ASSERT(AfxIsValidAddress(pNode, sizeof(CNode))); rPosition = (POSITION) pNode->pPrev; return pNode->data; } template AFX_INLINE const TYPE& CList::GetPrev(POSITION& rPosition) const // return *Position-- { CNode* pNode = (CNode*) rPosition; ASSERT(AfxIsValidAddress(pNode, sizeof(CNode))); rPosition = (POSITION) pNode->pPrev; return pNode->data; } template AFX_INLINE TYPE& CList::GetAt(POSITION position) { CNode* pNode = (CNode*) position; ASSERT(AfxIsValidAddress(pNode, sizeof(CNode))); return pNode->data; } template AFX_INLINE const TYPE& CList::GetAt(POSITION position) const { CNode* pNode = (CNode*) position; ASSERT(AfxIsValidAddress(pNode, sizeof(CNode))); return pNode->data; } template AFX_INLINE void CList::SetAt(POSITION pos, ARG_TYPE newElement) { CNode* pNode = (CNode*) pos; ASSERT(AfxIsValidAddress(pNode, sizeof(CNode))); pNode->data = newElement; } template CList::CList(INT_PTR nBlockSize) { ASSERT(nBlockSize > 0); m_nCount = 0; m_pNodeHead = m_pNodeTail = m_pNodeFree = NULL; m_pBlocks = NULL; m_nBlockSize = nBlockSize; } template void CList::RemoveAll() { ASSERT_VALID(this); // destroy elements CNode* pNode; for (pNode = m_pNodeHead; pNode != NULL; pNode = pNode->pNext) pNode->data.~TYPE(); m_nCount = 0; m_pNodeHead = m_pNodeTail = m_pNodeFree = NULL; m_pBlocks->FreeDataChain(); m_pBlocks = NULL; } template CList::~CList() { RemoveAll(); ASSERT(m_nCount == 0); } ///////////////////////////////////////////////////////////////////////////// // Node helpers // // Implementation note: CNode's are stored in CPlex blocks and // chained together. Free blocks are maintained in a singly linked list // using the 'pNext' member of CNode with 'm_pNodeFree' as the head. // Used blocks are maintained in a doubly linked list using both 'pNext' // and 'pPrev' as links and 'm_pNodeHead' and 'm_pNodeTail' // as the head/tail. // // We never free a CPlex block unless the List is destroyed or RemoveAll() // is used - so the total number of CPlex blocks may grow large depending // on the maximum past size of the list. // template typename CList::CNode* CList::NewNode(CNode* pPrev, CNode* pNext) { if (m_pNodeFree == NULL) { // add another block CPlex* pNewBlock = CPlex::Create(m_pBlocks, m_nBlockSize, sizeof(CNode)); // chain them into free list CNode* pNode = (CNode*) pNewBlock->data(); // free in reverse order to make it easier to debug pNode += m_nBlockSize - 1; for (INT_PTR i = m_nBlockSize-1; i >= 0; i--, pNode--) { pNode->pNext = m_pNodeFree; m_pNodeFree = pNode; } } ENSURE(m_pNodeFree != NULL); // we must have something CList::CNode* pNode = m_pNodeFree; m_pNodeFree = m_pNodeFree->pNext; pNode->pPrev = pPrev; pNode->pNext = pNext; m_nCount++; ASSERT(m_nCount > 0); // make sure we don't overflow #pragma push_macro("new") #undef new ::new( (void*)( &pNode->data ) ) TYPE; #pragma pop_macro("new") return pNode; } template void CList::FreeNode(CNode* pNode) { pNode->data.~TYPE(); pNode->pNext = m_pNodeFree; m_pNodeFree = pNode; m_nCount--; ASSERT(m_nCount >= 0); // make sure we don't underflow // if no more elements, cleanup completely if (m_nCount == 0) RemoveAll(); } template POSITION CList::AddHead(ARG_TYPE newElement) { ASSERT_VALID(this); CNode* pNewNode = NewNode(NULL, m_pNodeHead); pNewNode->data = newElement; if (m_pNodeHead != NULL) m_pNodeHead->pPrev = pNewNode; else m_pNodeTail = pNewNode; m_pNodeHead = pNewNode; return (POSITION) pNewNode; } template POSITION CList::AddTail(ARG_TYPE newElement) { ASSERT_VALID(this); CNode* pNewNode = NewNode(m_pNodeTail, NULL); pNewNode->data = newElement; if (m_pNodeTail != NULL) m_pNodeTail->pNext = pNewNode; else m_pNodeHead = pNewNode; m_pNodeTail = pNewNode; return (POSITION) pNewNode; } template void CList::AddHead(CList* pNewList) { ASSERT_VALID(this); ENSURE(pNewList != NULL); ASSERT_VALID(pNewList); // add a list of same elements to head (maintain order) POSITION pos = pNewList->GetTailPosition(); while (pos != NULL) AddHead(pNewList->GetPrev(pos)); } template void CList::AddTail(CList* pNewList) { ASSERT_VALID(this); ENSURE(pNewList != NULL); ASSERT_VALID(pNewList); // add a list of same elements POSITION pos = pNewList->GetHeadPosition(); while (pos != NULL) AddTail(pNewList->GetNext(pos)); } template TYPE CList::RemoveHead() { ASSERT_VALID(this); ASSERT(m_pNodeHead != NULL); // don't call on empty list !!! ASSERT(AfxIsValidAddress(m_pNodeHead, sizeof(CNode))); CNode* pOldNode = m_pNodeHead; TYPE returnValue = pOldNode->data; m_pNodeHead = pOldNode->pNext; if (m_pNodeHead != NULL) m_pNodeHead->pPrev = NULL; else m_pNodeTail = NULL; FreeNode(pOldNode); return returnValue; } template TYPE CList::RemoveTail() { ASSERT_VALID(this); ASSERT(m_pNodeTail != NULL); // don't call on empty list !!! ASSERT(AfxIsValidAddress(m_pNodeTail, sizeof(CNode))); CNode* pOldNode = m_pNodeTail; TYPE returnValue = pOldNode->data; m_pNodeTail = pOldNode->pPrev; if (m_pNodeTail != NULL) m_pNodeTail->pNext = NULL; else m_pNodeHead = NULL; FreeNode(pOldNode); return returnValue; } template POSITION CList::InsertBefore(POSITION position, ARG_TYPE newElement) { ASSERT_VALID(this); if (position == NULL) return AddHead(newElement); // insert before nothing -> head of the list // Insert it before position CNode* pOldNode = (CNode*) position; CNode* pNewNode = NewNode(pOldNode->pPrev, pOldNode); pNewNode->data = newElement; if (pOldNode->pPrev != NULL) { ASSERT(AfxIsValidAddress(pOldNode->pPrev, sizeof(CNode))); pOldNode->pPrev->pNext = pNewNode; } else { ASSERT(pOldNode == m_pNodeHead); m_pNodeHead = pNewNode; } pOldNode->pPrev = pNewNode; return (POSITION) pNewNode; } template POSITION CList::InsertAfter(POSITION position, ARG_TYPE newElement) { ASSERT_VALID(this); if (position == NULL) return AddTail(newElement); // insert after nothing -> tail of the list // Insert it before position CNode* pOldNode = (CNode*) position; ASSERT(AfxIsValidAddress(pOldNode, sizeof(CNode))); CNode* pNewNode = NewNode(pOldNode, pOldNode->pNext); pNewNode->data = newElement; if (pOldNode->pNext != NULL) { ASSERT(AfxIsValidAddress(pOldNode->pNext, sizeof(CNode))); pOldNode->pNext->pPrev = pNewNode; } else { ASSERT(pOldNode == m_pNodeTail); m_pNodeTail = pNewNode; } pOldNode->pNext = pNewNode; return (POSITION) pNewNode; } template void CList::RemoveAt(POSITION position) { ASSERT_VALID(this); CNode* pOldNode = (CNode*) position; ASSERT(AfxIsValidAddress(pOldNode, sizeof(CNode))); // remove pOldNode from list if (pOldNode == m_pNodeHead) { m_pNodeHead = pOldNode->pNext; } else { ASSERT(AfxIsValidAddress(pOldNode->pPrev, sizeof(CNode))); pOldNode->pPrev->pNext = pOldNode->pNext; } if (pOldNode == m_pNodeTail) { m_pNodeTail = pOldNode->pPrev; } else { ASSERT(AfxIsValidAddress(pOldNode->pNext, sizeof(CNode))); pOldNode->pNext->pPrev = pOldNode->pPrev; } FreeNode(pOldNode); } template POSITION CList::FindIndex(INT_PTR nIndex) const { ASSERT_VALID(this); if (nIndex >= m_nCount || nIndex < 0) return NULL; // went too far CNode* pNode = m_pNodeHead; while (nIndex--) { ASSERT(AfxIsValidAddress(pNode, sizeof(CNode))); pNode = pNode->pNext; } return (POSITION) pNode; } template POSITION CList::Find(ARG_TYPE searchValue, POSITION startAfter) const { ASSERT_VALID(this); CNode* pNode = (CNode*) startAfter; if (pNode == NULL) { pNode = m_pNodeHead; // start at head } else { ASSERT(AfxIsValidAddress(pNode, sizeof(CNode))); pNode = pNode->pNext; // start after the one specified } for (; pNode != NULL; pNode = pNode->pNext) if (CompareElements(&pNode->data, &searchValue)) return (POSITION)pNode; return NULL; } template void CList::Serialize(CArchive& ar) { ASSERT_VALID(this); CObject::Serialize(ar); if (ar.IsStoring()) { ar.WriteCount(m_nCount); for (CNode* pNode = m_pNodeHead; pNode != NULL; pNode = pNode->pNext) { ASSERT(AfxIsValidAddress(pNode, sizeof(CNode))); TYPE* pData; /* * in some cases the & operator might be overloaded, and we cannot use it to obtain * the address of a given object. We then use the following trick to get the address */ pData = reinterpret_cast< TYPE* >( &reinterpret_cast< int& >( static_cast< TYPE& >( pNode->data ) ) ); SerializeElements(ar, pData, 1); } } else { DWORD_PTR nNewCount = ar.ReadCount(); while (nNewCount--) { TYPE newData[1]; SerializeElements(ar, newData, 1); AddTail(newData[0]); } } } #ifdef _DEBUG template void CList::Dump(CDumpContext& dc) const { CObject::Dump(dc); dc << "with " << m_nCount << " elements"; if (dc.GetDepth() > 0) { POSITION pos = GetHeadPosition(); while (pos != NULL) { TYPE temp[1]; temp[0] = ((CList*)this)->GetNext(pos); dc << "\n"; DumpElements(dc, temp, 1); } } dc << "\n"; } template void CList::AssertValid() const { CObject::AssertValid(); if (m_nCount == 0) { // empty list ASSERT(m_pNodeHead == NULL); ASSERT(m_pNodeTail == NULL); } else { // non-empty list ASSERT(AfxIsValidAddress(m_pNodeHead, sizeof(CNode))); ASSERT(AfxIsValidAddress(m_pNodeTail, sizeof(CNode))); } } #endif //_DEBUG ///////////////////////////////////////////////////////////////////////////// // CMap template class CMap : public CObject { public: // CPair struct CPair { const KEY key; VALUE value; protected: CPair( ARG_KEY keyval ) : key( keyval ) {} }; protected: // Association class CAssoc : public CPair { friend class CMap; CAssoc* pNext; UINT nHashValue; // needed for efficient iteration public: CAssoc( ARG_KEY key ) : CPair( key ) {} }; public: // Construction /* explicit */ CMap(INT_PTR nBlockSize = 10); // Attributes // number of elements INT_PTR GetCount() const; INT_PTR GetSize() const; BOOL IsEmpty() const; // Lookup BOOL Lookup(ARG_KEY key, VALUE& rValue) const; const CPair *PLookup(ARG_KEY key) const; CPair *PLookup(ARG_KEY key); // Operations // Lookup and add if not there VALUE& operator[](ARG_KEY key); // add a new (key, value) pair void SetAt(ARG_KEY key, ARG_VALUE newValue); // removing existing (key, ?) pair BOOL RemoveKey(ARG_KEY key); void RemoveAll(); // iterating all (key, value) pairs POSITION GetStartPosition() const; const CPair *PGetFirstAssoc() const; CPair *PGetFirstAssoc(); void GetNextAssoc(POSITION& rNextPosition, KEY& rKey, VALUE& rValue) const; const CPair *PGetNextAssoc(const CPair *pAssocRet) const; CPair *PGetNextAssoc(const CPair *pAssocRet); // advanced features for derived classes UINT GetHashTableSize() const; void InitHashTable(UINT hashSize, BOOL bAllocNow = TRUE); // Implementation protected: CAssoc** m_pHashTable; UINT m_nHashTableSize; INT_PTR m_nCount; CAssoc* m_pFreeList; struct CPlex* m_pBlocks; INT_PTR m_nBlockSize; CAssoc* NewAssoc(ARG_KEY key); void FreeAssoc(CAssoc*); CAssoc* GetAssocAt(ARG_KEY, UINT&, UINT&) const; public: ~CMap(); void Serialize(CArchive&); #ifdef _DEBUG void Dump(CDumpContext&) const; void AssertValid() const; #endif }; ///////////////////////////////////////////////////////////////////////////// // CMap inline functions template AFX_INLINE INT_PTR CMap::GetCount() const { return m_nCount; } template AFX_INLINE INT_PTR CMap::GetSize() const { return m_nCount; } template AFX_INLINE BOOL CMap::IsEmpty() const { return m_nCount == 0; } template AFX_INLINE void CMap::SetAt(ARG_KEY key, ARG_VALUE newValue) { (*this)[key] = newValue; } template AFX_INLINE POSITION CMap::GetStartPosition() const { return (m_nCount == 0) ? NULL : BEFORE_START_POSITION; } template const typename CMap::CPair* CMap::PGetFirstAssoc() const { ASSERT_VALID(this); if(m_nCount == 0) return NULL; ASSERT(m_pHashTable != NULL); // never call on empty map CAssoc* pAssocRet = (CAssoc*)BEFORE_START_POSITION; // find the first association for (UINT nBucket = 0; nBucket < m_nHashTableSize; nBucket++) if ((pAssocRet = m_pHashTable[nBucket]) != NULL) break; ASSERT(pAssocRet != NULL); // must find something return pAssocRet; } template typename CMap::CPair* CMap::PGetFirstAssoc() { ASSERT_VALID(this); if(m_nCount == 0) return NULL; ASSERT(m_pHashTable != NULL); // never call on empty map CAssoc* pAssocRet = (CAssoc*)BEFORE_START_POSITION; // find the first association for (UINT nBucket = 0; nBucket < m_nHashTableSize; nBucket++) if ((pAssocRet = m_pHashTable[nBucket]) != NULL) break; ASSERT(pAssocRet != NULL); // must find something return pAssocRet; } template AFX_INLINE UINT CMap::GetHashTableSize() const { return m_nHashTableSize; } ///////////////////////////////////////////////////////////////////////////// // CMap out-of-line functions template CMap::CMap(INT_PTR nBlockSize) { ASSERT(nBlockSize > 0); m_pHashTable = NULL; m_nHashTableSize = 17; // default size m_nCount = 0; m_pFreeList = NULL; m_pBlocks = NULL; m_nBlockSize = nBlockSize; } template void CMap::InitHashTable( UINT nHashSize, BOOL bAllocNow) // // Used to force allocation of a hash table or to override the default // hash table size of (which is fairly small) { ASSERT_VALID(this); ASSERT(m_nCount == 0); ASSERT(nHashSize > 0); if (m_pHashTable != NULL) { // free hash table delete[] m_pHashTable; m_pHashTable = NULL; } if (bAllocNow) { m_pHashTable = new CAssoc* [nHashSize]; ENSURE(m_pHashTable != NULL); memset(m_pHashTable, 0, sizeof(CAssoc*) * nHashSize); } m_nHashTableSize = nHashSize; } template void CMap::RemoveAll() { ASSERT_VALID(this); if (m_pHashTable != NULL) { // destroy elements (values and keys) for (UINT nHash = 0; nHash < m_nHashTableSize; nHash++) { CAssoc* pAssoc; for (pAssoc = m_pHashTable[nHash]; pAssoc != NULL; pAssoc = pAssoc->pNext) { pAssoc->CAssoc::~CAssoc(); //DestructElements(&pAssoc->value, 1); //DestructElements((KEY*)&pAssoc->key, 1); } } } // free hash table delete[] m_pHashTable; m_pHashTable = NULL; m_nCount = 0; m_pFreeList = NULL; m_pBlocks->FreeDataChain(); m_pBlocks = NULL; } template CMap::~CMap() { RemoveAll(); ASSERT(m_nCount == 0); } template typename CMap::CAssoc* CMap::NewAssoc(ARG_KEY key) { if (m_pFreeList == NULL) { // add another block CPlex* newBlock = CPlex::Create(m_pBlocks, m_nBlockSize, sizeof(CMap::CAssoc)); // chain them into free list CMap::CAssoc* pAssoc = (CMap::CAssoc*) newBlock->data(); // free in reverse order to make it easier to debug pAssoc += m_nBlockSize - 1; for (INT_PTR i = m_nBlockSize-1; i >= 0; i--, pAssoc--) { pAssoc->pNext = m_pFreeList; m_pFreeList = pAssoc; } } ENSURE(m_pFreeList != NULL); // we must have something CMap::CAssoc* pAssoc = m_pFreeList; // zero the memory CMap::CAssoc* pTemp = pAssoc->pNext; memset( pAssoc, 0, sizeof(CMap::CAssoc) ); pAssoc->pNext = pTemp; m_pFreeList = m_pFreeList->pNext; m_nCount++; ASSERT(m_nCount > 0); // make sure we don't overflow #pragma push_macro("new") #undef new ::new(pAssoc) CMap::CAssoc(key); #pragma pop_macro("new") // ConstructElements(&pAssoc->key, 1); // ConstructElements(&pAssoc->value, 1); // special construct values return pAssoc; } template void CMap::FreeAssoc(CAssoc* pAssoc) { pAssoc->CAssoc::~CAssoc(); // DestructElements(&pAssoc->value, 1); // DestructElements(&pAssoc->key, 1); pAssoc->pNext = m_pFreeList; m_pFreeList = pAssoc; m_nCount--; ASSERT(m_nCount >= 0); // make sure we don't underflow // if no more elements, cleanup completely if (m_nCount == 0) RemoveAll(); } template typename CMap::CAssoc* CMap::GetAssocAt(ARG_KEY key, UINT& nHashBucket, UINT& nHashValue) const // find association (or return NULL) { nHashValue = HashKey(key); nHashBucket = nHashValue % m_nHashTableSize; if (m_pHashTable == NULL) return NULL; // see if it exists CAssoc* pAssoc; for (pAssoc = m_pHashTable[nHashBucket]; pAssoc != NULL; pAssoc = pAssoc->pNext) { if (pAssoc->nHashValue == nHashValue && CompareElements(&pAssoc->key, &key)) return pAssoc; } return NULL; } template BOOL CMap::Lookup(ARG_KEY key, VALUE& rValue) const { ASSERT_VALID(this); UINT nHashBucket, nHashValue; CAssoc* pAssoc = GetAssocAt(key, nHashBucket, nHashValue); if (pAssoc == NULL) return FALSE; // not in map rValue = pAssoc->value; return TRUE; } template const typename CMap::CPair* CMap::PLookup(ARG_KEY key) const { ASSERT_VALID(this); UINT nHashBucket, nHashValue; CAssoc* pAssoc = GetAssocAt(key, nHashBucket, nHashValue); return pAssoc; } template typename CMap::CPair* CMap::PLookup(ARG_KEY key) { ASSERT_VALID(this); UINT nHashBucket, nHashValue; CAssoc* pAssoc = GetAssocAt(key, nHashBucket, nHashValue); return pAssoc; } template VALUE& CMap::operator[](ARG_KEY key) { ASSERT_VALID(this); UINT nHashBucket, nHashValue; CAssoc* pAssoc; if ((pAssoc = GetAssocAt(key, nHashBucket, nHashValue)) == NULL) { if (m_pHashTable == NULL) InitHashTable(m_nHashTableSize); ENSURE(m_pHashTable); // it doesn't exist, add a new Association pAssoc = NewAssoc(key); pAssoc->nHashValue = nHashValue; //'pAssoc->value' is a constructed object, nothing more // put into hash table pAssoc->pNext = m_pHashTable[nHashBucket]; m_pHashTable[nHashBucket] = pAssoc; } return pAssoc->value; // return new reference } template BOOL CMap::RemoveKey(ARG_KEY key) // remove key - return TRUE if removed { ASSERT_VALID(this); if (m_pHashTable == NULL) return FALSE; // nothing in the table UINT nHashValue; CAssoc** ppAssocPrev; nHashValue = HashKey(key); ppAssocPrev = &m_pHashTable[nHashValue%m_nHashTableSize]; CAssoc* pAssoc; for (pAssoc = *ppAssocPrev; pAssoc != NULL; pAssoc = pAssoc->pNext) { if ((pAssoc->nHashValue == nHashValue) && CompareElements(&pAssoc->key, &key)) { // remove it *ppAssocPrev = pAssoc->pNext; // remove from list FreeAssoc(pAssoc); return TRUE; } ppAssocPrev = &pAssoc->pNext; } return FALSE; // not found } template void CMap::GetNextAssoc(POSITION& rNextPosition, KEY& rKey, VALUE& rValue) const { ASSERT_VALID(this); ENSURE(m_pHashTable != NULL); // never call on empty map CAssoc* pAssocRet = (CAssoc*)rNextPosition; ENSURE(pAssocRet != NULL); if (pAssocRet == (CAssoc*) BEFORE_START_POSITION) { // find the first association for (UINT nBucket = 0; nBucket < m_nHashTableSize; nBucket++) { if ((pAssocRet = m_pHashTable[nBucket]) != NULL) { break; } } ENSURE(pAssocRet != NULL); // must find something } // find next association ASSERT(AfxIsValidAddress(pAssocRet, sizeof(CAssoc))); CAssoc* pAssocNext; if ((pAssocNext = pAssocRet->pNext) == NULL) { // go to next bucket for (UINT nBucket = (pAssocRet->nHashValue % m_nHashTableSize) + 1; nBucket < m_nHashTableSize; nBucket++) if ((pAssocNext = m_pHashTable[nBucket]) != NULL) break; } rNextPosition = (POSITION) pAssocNext; // fill in return data rKey = pAssocRet->key; rValue = pAssocRet->value; } template const typename CMap::CPair* CMap::PGetNextAssoc(const typename CMap::CPair* pPairRet) const { ASSERT_VALID(this); CAssoc* pAssocRet = (CAssoc*)pPairRet; ASSERT(m_pHashTable != NULL); // never call on empty map ASSERT(pAssocRet != NULL); if(m_pHashTable == NULL || pAssocRet == NULL) return NULL; ASSERT(pAssocRet != (CAssoc*)BEFORE_START_POSITION); // find next association ASSERT(AfxIsValidAddress(pAssocRet, sizeof(CAssoc))); CAssoc* pAssocNext; if ((pAssocNext = pAssocRet->pNext) == NULL) { // go to next bucket for (UINT nBucket = (pAssocRet->nHashValue % m_nHashTableSize) + 1; nBucket < m_nHashTableSize; nBucket++) if ((pAssocNext = m_pHashTable[nBucket]) != NULL) break; } return pAssocNext; } template typename CMap::CPair* CMap::PGetNextAssoc(const typename CMap::CPair* pPairRet) { ASSERT_VALID(this); CAssoc* pAssocRet = (CAssoc*)pPairRet; ASSERT(m_pHashTable != NULL); // never call on empty map ASSERT(pAssocRet != NULL); if(m_pHashTable == NULL || pAssocRet == NULL) return NULL; ASSERT(pAssocRet != (CAssoc*)BEFORE_START_POSITION); // find next association ASSERT(AfxIsValidAddress(pAssocRet, sizeof(CAssoc))); CAssoc* pAssocNext; if ((pAssocNext = pAssocRet->pNext) == NULL) { // go to next bucket for (UINT nBucket = (pAssocRet->nHashValue % m_nHashTableSize) + 1; nBucket < m_nHashTableSize; nBucket++) if ((pAssocNext = m_pHashTable[nBucket]) != NULL) break; } return pAssocNext; } template void CMap::Serialize(CArchive& ar) { ASSERT_VALID(this); CObject::Serialize(ar); if (ar.IsStoring()) { ar.WriteCount(m_nCount); if (m_nCount == 0) return; // nothing more to do ASSERT(m_pHashTable != NULL); for (UINT nHash = 0; nHash < m_nHashTableSize; nHash++) { CAssoc* pAssoc; for (pAssoc = m_pHashTable[nHash]; pAssoc != NULL; pAssoc = pAssoc->pNext) { KEY* pKey; VALUE* pValue; /* * in some cases the & operator might be overloaded, and we cannot use it to * obtain the address of a given object. We then use the following trick to * get the address */ pKey = reinterpret_cast< KEY* >( &reinterpret_cast< int& >( const_cast< KEY& > ( static_cast< const KEY& >( pAssoc->key ) ) ) ); pValue = reinterpret_cast< VALUE* >( &reinterpret_cast< int& >( static_cast< VALUE& >( pAssoc->value ) ) ); SerializeElements(ar, pKey, 1); SerializeElements(ar, pValue, 1); } } } else { DWORD_PTR nNewCount = ar.ReadCount(); while (nNewCount--) { KEY newKey[1]; VALUE newValue[1]; SerializeElements(ar, newKey, 1); SerializeElements(ar, newValue, 1); SetAt(newKey[0], newValue[0]); } } } #ifdef _DEBUG template void CMap::Dump(CDumpContext& dc) const { CObject::Dump(dc); dc << "with " << m_nCount << " elements"; if (dc.GetDepth() > 0) { // Dump in format "[key] -> value" KEY key[1]; VALUE val[1]; POSITION pos = GetStartPosition(); while (pos != NULL) { GetNextAssoc(pos, key[0], val[0]); dc << "\n\t["; DumpElements(dc, key, 1); dc << "] = "; DumpElements(dc, val, 1); } } dc << "\n"; } template void CMap::AssertValid() const { CObject::AssertValid(); ASSERT(m_nHashTableSize > 0); ASSERT(m_nCount == 0 || m_pHashTable != NULL); // non-empty map should have hash table } #endif //_DEBUG ///////////////////////////////////////////////////////////////////////////// // CTypedPtrArray template class CTypedPtrArray : public BASE_CLASS { public: // Accessing elements TYPE GetAt(INT_PTR nIndex) const { return (TYPE)BASE_CLASS::GetAt(nIndex); } TYPE& ElementAt(INT_PTR nIndex) { return (TYPE&)BASE_CLASS::ElementAt(nIndex); } void SetAt(INT_PTR nIndex, TYPE ptr) { BASE_CLASS::SetAt(nIndex, ptr); } // Potentially growing the array void SetAtGrow(INT_PTR nIndex, TYPE newElement) { BASE_CLASS::SetAtGrow(nIndex, newElement); } INT_PTR Add(TYPE newElement) { return BASE_CLASS::Add(newElement); } INT_PTR Append(const CTypedPtrArray& src) { return BASE_CLASS::Append(src); } void Copy(const CTypedPtrArray& src) { BASE_CLASS::Copy(src); } // Operations that move elements around void InsertAt(INT_PTR nIndex, TYPE newElement, INT_PTR nCount = 1) { BASE_CLASS::InsertAt(nIndex, newElement, nCount); } void InsertAt(INT_PTR nStartIndex, CTypedPtrArray* pNewArray) { BASE_CLASS::InsertAt(nStartIndex, pNewArray); } // overloaded operator helpers TYPE operator[](INT_PTR nIndex) const { return (TYPE)BASE_CLASS::operator[](nIndex); } TYPE& operator[](INT_PTR nIndex) { return (TYPE&)BASE_CLASS::operator[](nIndex); } }; ///////////////////////////////////////////////////////////////////////////// // CTypedPtrList template class _CTypedPtrList : public BASE_CLASS { public: // Construction _CTypedPtrList(INT_PTR nBlockSize = 10) : BASE_CLASS(nBlockSize) { } // peek at head or tail TYPE& GetHead() { return (TYPE&)BASE_CLASS::GetHead(); } TYPE GetHead() const { return (TYPE)BASE_CLASS::GetHead(); } TYPE& GetTail() { return (TYPE&)BASE_CLASS::GetTail(); } TYPE GetTail() const { return (TYPE)BASE_CLASS::GetTail(); } // get head or tail (and remove it) - don't call on empty list! TYPE RemoveHead() { return (TYPE)BASE_CLASS::RemoveHead(); } TYPE RemoveTail() { return (TYPE)BASE_CLASS::RemoveTail(); } // iteration TYPE& GetNext(POSITION& rPosition) { return (TYPE&)BASE_CLASS::GetNext(rPosition); } TYPE GetNext(POSITION& rPosition) const { return (TYPE)BASE_CLASS::GetNext(rPosition); } TYPE& GetPrev(POSITION& rPosition) { return (TYPE&)BASE_CLASS::GetPrev(rPosition); } TYPE GetPrev(POSITION& rPosition) const { return (TYPE)BASE_CLASS::GetPrev(rPosition); } // getting/modifying an element at a given position TYPE& GetAt(POSITION position) { return (TYPE&)BASE_CLASS::GetAt(position); } TYPE GetAt(POSITION position) const { return (TYPE)BASE_CLASS::GetAt(position); } void SetAt(POSITION pos, TYPE newElement) { BASE_CLASS::SetAt(pos, newElement); } // inserting before or after a given position POSITION InsertBefore(POSITION position, TYPE newElement) { return BASE_CLASS::InsertBefore(position, newElement); } POSITION InsertAfter(POSITION position, TYPE newElement) { return BASE_CLASS::InsertAfter(position, newElement); } // transfer before or after a given position // Transfer semantics ensure no leakage by deleting the element in the case of an exception POSITION TransferInsertBefore(POSITION position, TYPE newElement) { try { return BASE_CLASS::InsertBefore(position, newElement); } catch(...) { delete newElement; throw; } } POSITION TransferInsertAfter(POSITION position, TYPE newElement) { try { return BASE_CLASS::InsertAfter(position, newElement); } catch(...) { delete newElement; throw; } } }; template class CTypedPtrList : public _CTypedPtrList { public: // Construction CTypedPtrList(INT_PTR nBlockSize = 10) : _CTypedPtrList(nBlockSize) { } // add before head or after tail POSITION AddHead(TYPE newElement) { return BASE_CLASS::AddHead(newElement); } POSITION AddTail(TYPE newElement) { return BASE_CLASS::AddTail(newElement); } // transfer add before head or tail POSITION TransferAddHead(TYPE newElement) { try { return BASE_CLASS::AddHead(newElement); } catch(...) { delete newElement; throw; } } POSITION TransferAddTail(TYPE newElement) { try { return BASE_CLASS::AddTail(newElement); } catch(...) { delete newElement; throw; } } // add another list of elements before head or after tail void AddHead(CTypedPtrList* pNewList) { BASE_CLASS::AddHead(pNewList); } void AddTail(CTypedPtrList* pNewList) { BASE_CLASS::AddTail(pNewList); } }; // need specialized version for CObList because of AddHead/Tail ambiguity template<> class CTypedPtrList : public _CTypedPtrList { public: // Construction CTypedPtrList(INT_PTR nBlockSize = 10) : _CTypedPtrList(nBlockSize) { } // add before head or after tail POSITION AddHead(CObList* newElement) { return _CTypedPtrList::AddHead((CObject*)newElement); } POSITION AddTail(CObList* newElement) { return _CTypedPtrList::AddTail((CObject*)newElement); } // add another list of elements before head or after tail void AddHead(CTypedPtrList* pNewList) { _CTypedPtrList::AddHead(pNewList); } void AddTail(CTypedPtrList* pNewList) { _CTypedPtrList::AddTail(pNewList); } }; // need specialized version for CPtrList because of AddHead/Tail ambiguity template<> class CTypedPtrList : public _CTypedPtrList { public: // Construction CTypedPtrList(INT_PTR nBlockSize = 10) : _CTypedPtrList(nBlockSize) { } // add before head or after tail POSITION AddHead(CPtrList* newElement) { return _CTypedPtrList::AddHead((void*)newElement); } POSITION AddTail(CPtrList* newElement) { return _CTypedPtrList::AddTail((void*)newElement); } // add another list of elements before head or after tail void AddHead(CTypedPtrList* pNewList) { _CTypedPtrList::AddHead(pNewList); } void AddTail(CTypedPtrList* pNewList) { _CTypedPtrList::AddTail(pNewList); } }; ///////////////////////////////////////////////////////////////////////////// // CTypedPtrMap template class CTypedPtrMap : public BASE_CLASS { public: // Construction CTypedPtrMap(INT_PTR nBlockSize = 10) : BASE_CLASS(nBlockSize) { } // Lookup BOOL Lookup(typename BASE_CLASS::BASE_ARG_KEY key, VALUE& rValue) const { return BASE_CLASS::Lookup(key, (BASE_CLASS::BASE_VALUE&)rValue); } // Lookup and add if not there VALUE& operator[](typename BASE_CLASS::BASE_ARG_KEY key) { return (VALUE&)BASE_CLASS::operator[](key); } // add a new key (key, value) pair void SetAt(KEY key, VALUE newValue) { BASE_CLASS::SetAt(key, newValue); } // removing existing (key, ?) pair BOOL RemoveKey(KEY key) { return BASE_CLASS::RemoveKey(key); } // iteration void GetNextAssoc(POSITION& rPosition, KEY& rKey, VALUE& rValue) const { BASE_CLASS::GetNextAssoc(rPosition, (BASE_CLASS::BASE_KEY&)rKey, (BASE_CLASS::BASE_VALUE&)rValue); } }; ///////////////////////////////////////////////////////////////////////////// #undef THIS_FILE #define THIS_FILE __FILE__ #pragma pop_macro("new") #ifdef _AFX_PACKING #pragma pack(pop) #endif #ifdef _AFX_MINREBUILD #pragma component(minrebuild, on) #endif #ifndef _AFX_FULLTYPEINFO #pragma component(mintypeinfo, off) #endif #pragma warning( pop ) #endif //__AFXTEMPL_H__ /////////////////////////////////////////////////////////////////////////////