/* Template class for doing a simple linked list ... */ #ifndef _TLIST_H #define _TLIST_H // the enum marker that remembers the current position typedef void * CLISTPOS; // template class for providing a doubly linked of pointers to nodes template< class NODETYPE > class CList { protected: struct CNode { NODETYPE * m_pData; CNode * m_pPrev; CNode * m_pNext; }; public: CList(); ~CList(); CLISTPOS GetHeadPosition(); NODETYPE * GetNext( CLISTPOS & rpCurPos ); int GetCount(); void RemoveAt( CLISTPOS pPos ); void RemoveAll( void ); CLISTPOS FindIndex( int iIndex ); CLISTPOS AddTail( NODETYPE * pData ); CLISTPOS AddBefore( CLISTPOS pPos, NODETYPE * pData ); #ifdef DEBUG void ValidateList(); #define VALIDATELIST() ValidateList() #else #define VALIDATELIST() #endif protected: CNode * m_pHead; CNode * m_pTail; }; ///////////////////////////////////////////////////////////////////////////////////////// template< class NODETYPE > CList::CList() { m_pHead = NULL; m_pTail = NULL; } ///////////////////////////////////////////////////////////////////////////////////////// template< class NODETYPE > CList::~CList() { RemoveAll(); } ///////////////////////////////////////////////////////////////////////////////////////// template< class NODETYPE > CLISTPOS CList::GetHeadPosition( ) { return (CLISTPOS) m_pHead; } ///////////////////////////////////////////////////////////////////////////////////////// template< class NODETYPE > NODETYPE * CList::GetNext( CLISTPOS & rpCurPos ) { ASSERT( rpCurPos != NULL ); CNode * pCur = (CNode *) rpCurPos; NODETYPE * pData = pCur->m_pData; rpCurPos = (CLISTPOS) pCur->m_pNext; return pData; } ///////////////////////////////////////////////////////////////////////////////////////// template< class NODETYPE > int CList::GetCount() { int iLength = 0; CNode * pCur = m_pHead; while ( pCur != NULL ) { pCur = pCur->m_pNext; iLength ++; } return iLength; } ///////////////////////////////////////////////////////////////////////////////////////// template< class NODETYPE > void CList::RemoveAt( CLISTPOS pPos ) { ASSERT( pPos != NULL ); #ifdef _DEBUG // scan the list to ensure the marker is valid.... CNode * pCur = m_pHead; while ( pCur != NULL ) { if ( pCur == (CNode *) pPos ) { break; } pCur = pCur->m_pNext; } ASSERT( pCur != NULL ) #endif CNode * pRealPos = (CNode *) pPos; if ( pRealPos->m_pPrev == NULL ) { // we are at the start of the list m_pHead = pRealPos->m_pNext; } else { // link the prev one to the next one (bypassing this one) pRealPos->m_pPrev->m_pNext = pRealPos->m_pNext; } if ( pRealPos->m_pNext == NULL ) { // we are at the end of the list m_pTail = pRealPos->m_pPrev; } else { // link the next to the prev (bypassing this one) pRealPos->m_pNext->m_pPrev = pRealPos->m_pPrev; } LocalFree( pRealPos ); VALIDATELIST(); } ///////////////////////////////////////////////////////////////////////////////////////// template< class NODETYPE > CLISTPOS CList::FindIndex( int iIndex ) { ASSERT( iIndex >= 0 ); CNode * pCur = m_pHead; while ( iIndex > 0 && pCur != NULL ) { pCur = pCur->m_pNext; iIndex --; } return (CLISTPOS)(iIndex == 0 ? pCur : NULL ); } ///////////////////////////////////////////////////////////////////////////////////////// template< class NODETYPE > void CList::RemoveAll( void ) { // note we will not free the data elements, the client must do this... CNode * pCur = m_pHead; while (pCur != NULL ) { CNode * pTmp = pCur->m_pNext; LocalFree( pCur ); pCur = pTmp; } m_pHead = m_pTail = NULL; } ///////////////////////////////////////////////////////////////////////////////////////// template< class NODETYPE > CLISTPOS CList::AddTail( NODETYPE * pData ) { CNode * pCurTail = m_pTail; CNode * pNewNode = (CNode * ) LocalAlloc( GPTR, sizeof( CNode )); if ( pNewNode == NULL ) { return NULL; } pNewNode->m_pData = pData; pNewNode->m_pPrev = pCurTail; pNewNode->m_pNext = NULL; m_pTail = pNewNode; if ( pCurTail != NULL ) { // we are not an empty list pCurTail->m_pNext = pNewNode; } else { m_pHead = pNewNode; } VALIDATELIST(); return (CLISTPOS) pNewNode; } ///////////////////////////////////////////////////////////////////////////////////////// template< class NODETYPE > CLISTPOS CList::AddBefore( CLISTPOS pPos, NODETYPE * pData ) { if ( !pPos ) { return NULL; } CNode * pPrev = (CNode *) pPos; CNode * pNewNode = (CNode * ) LocalAlloc( GPTR, sizeof( CNode )); if ( pNewNode == NULL ) { return NULL; } pNewNode->m_pData = pData; pNewNode->m_pPrev = pPrev->m_pPrev; pNewNode->m_pNext = pPrev; if ( pPrev->m_pPrev != NULL ) { pPrev->m_pPrev->m_pNext = pNewNode; } else { // must be at the start of the list... m_pHead = pNewNode; } pPrev->m_pPrev = pNewNode; VALIDATELIST(); return (CLISTPOS) pNewNode; } ///////////////////////////////////////////////////////////////////////////////////////// #ifdef DEBUG template< class NODETYPE > void CList::ValidateList( ) { CNode * pPos = m_pHead; while ( pPos ) { ASSERT( pPos->m_pData ); if ( pPos != m_pHead ) { ASSERT( pPos->m_pPrev ); } pPos = pPos->m_pNext; } pPos = m_pTail; while ( pPos ) { ASSERT( pPos->m_pData ); if ( pPos != m_pTail ) { ASSERT( pPos->m_pNext ); } pPos = pPos->m_pPrev; } if ( m_pHead || m_pTail ) { ASSERT( !m_pHead->m_pPrev ); ASSERT( m_pTail ); ASSERT( m_pHead ); ASSERT( !m_pTail->m_pNext ); } } #endif #endif