//----------------------------------------------------------------------------- // File: Linklist.h // Desc: Linked list class. // Copyright (C) Microsoft Corporation. All rights reserved. //----------------------------------------------------------------------------- // Notes: // // This class template implements a simple double-linked list. // Objects are held as pointers. (Does not use STL's copy semantics.) // The Clear() method takes a functor object that releases the objects. #pragma once #include #include #include template class List { protected: // Nodes in the linked list struct Node { Node *prev; Node *next; T *item; Node() : prev(NULL), next(NULL), item(NULL) { } Node(T* item) : prev(NULL), next(NULL) { this->item = item; } T* Item() const { return item; } }; protected: Node m_anchor; // Anchor node for the linked list. DWORD m_count; // Number of items in the list Node *m_pEnum; // Enumeration node void InvalidateEnumerator() { m_pEnum = NULL; } Node* Front() const { return m_anchor.next; } Node* Back() const { return m_anchor.prev; } virtual HRESULT InsertAfter(T* item, Node *pBefore) { if (item == NULL || pBefore == NULL) { return E_POINTER; } Node *pNode = new Node(item); if (pNode == NULL) { return E_OUTOFMEMORY; } Node *pAfter = pBefore->next; pBefore->next = pNode; pAfter->prev = pNode; pNode->prev = pBefore; pNode->next = pAfter; m_count++; InvalidateEnumerator(); return S_OK; } virtual HRESULT GetItem(Node *pNode, T** ppItem) { if (pNode == NULL || ppItem == NULL) { return E_POINTER; } if (pNode->item) { *ppItem = pNode->item; return S_OK; } else { return E_INVALIDARG; } } // RemoveItem: // Removes a node and optionally returns the item. // ppItem can be NULL. virtual HRESULT RemoveItem(Node *pNode, T **ppItem) { if (pNode == NULL) { return E_POINTER; } if (pNode == &m_anchor) { return E_INVALIDARG; } T* item = NULL; // anchor points back to the previous thing m_anchor.prev = pNode->prev; // the previous thing points to the anchor pNode->prev->next = &m_anchor; item = pNode->item; delete pNode; m_count--; if (ppItem) { *ppItem = item; } InvalidateEnumerator(); return S_OK; } public: List() { m_anchor.next = &m_anchor; m_anchor.prev = &m_anchor; m_count = 0; m_pEnum = NULL; } virtual ~List() { } // Insertion functions HRESULT InsertBack(T* item) { return InsertAfter(item, m_anchor.prev); } HRESULT InsertFront(T* item) { return InsertAfter(item, &m_anchor); } // RemoveBack: Removes the tail of the list and returns the value. HRESULT RemoveBack(T **ppItem) { if (IsEmpty()) { return E_FAIL; } else { return RemoveItem(Back(), ppItem); } } // RemoveFront: Removes the head of the list and returns the value. HRESULT RemoveFront(T **ppItem) { if (IsEmpty()) { return E_FAIL; } else { return RemoveItem(Front(), ppItem); } } HRESULT PopFront() { return RemoveFront(NULL); } HRESULT PopBack() { return RemoveBack(NULL); } HRESULT GetBack(T **ppItem) { if (IsEmpty()) { return E_FAIL; } else { return GetItem(Back(), ppItem); } } HRESULT GetFront(T **ppItem) { if (IsEmpty()) { return E_FAIL; } else { return GetItem(Front(), ppItem); } } // GetCount: Returns the number of items in the list. DWORD GetCount() const { return m_count; } bool IsEmpty() const { return (GetCount() == 0); } // Clear: Takes a functor object whose operator() // frees the object on the list. template void Clear(FN& clear_fn) { Node *n = m_anchor.next; // Delete the nodes while (n != &m_anchor) { clear_fn(n->item); Node *tmp = n->next; delete n; n = tmp; } // Reset the anchor to point at itself m_anchor.next = &m_anchor; m_anchor.prev = &m_anchor; m_count = 0; } // Enumerator functions void ResetEnumerator() { m_pEnum = Front(); } HRESULT Next(T **ppItem) { if (ppItem == NULL) { return E_POINTER; } if (m_pEnum == NULL) { return E_FAIL; // Needs reset } if (m_pEnum == &m_anchor) { return __HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS); } HRESULT hr = GetItem(m_pEnum, ppItem); if (SUCCEEDED(hr)) { m_pEnum = m_pEnum->next; } return hr; } }; // Typical functors for Clear method. // ComAutoRelease: Releases COM pointers. // MemDelete: Deletes pointers to new'd memory. class ComAutoRelease { public: void operator()(IUnknown *p) { if (p) { p->Release(); } } }; class MemDelete { public: void operator()(void *p) { if (p) { delete p; } } }; //ComPtrList class //Derived class that makes it safer to store COM pointers in the List<> class. //It automatically AddRef's the pointers that are inserted onto the list //(unless the insertion method fails). //T must be a COM interface type. template class ComPtrList : public List { public: void Clear() { List::Clear(ComAutoRelease()); } protected: HRESULT InsertAfter(T* item, Node *pBefore) { item->AddRef(); HRESULT hr = List::InsertAfter(item, pBefore); if (FAILED(hr)) { item->Release(); } return hr; } HRESULT GetItem(Node *pNode, T** ppItem) { HRESULT hr = List::GetItem(pNode, ppItem); if (SUCCEEDED(hr)) { (*ppItem)->AddRef(); } return hr; } HRESULT RemoveItem(Node *pNode, T **ppItem) { // ppItem can be NULL, but we need to get the // item so that we can release it. // If ppItem is not NULL, we will AddRef it on the way out/ T* item = NULL; HRESULT hr = List::RemoveItem(pNode, &item); if (SUCCEEDED(hr)) { if (ppItem) { *ppItem = item; (*ppItem)->AddRef(); } item->Release(); } return hr; } }; template class ComPtrListEx { protected: typedef T* Ptr; // Nodes in the linked list struct Node { Node *prev; Node *next; Ptr item; Node() : prev(NULL), next(NULL), item(NULL) { } Node(Ptr item) : prev(NULL), next(NULL) { this->item = item; if (item) { item->AddRef(); } } ~Node() { if (item) { item->Release(); } } Ptr Item() const { return item; } }; public: // Object for enumerating the list. class POSITION { friend class ComPtrListEx; public: POSITION() : pNode(NULL) { } bool operator==(const POSITION &p) const { return pNode == p.pNode; } bool operator!=(const POSITION &p) const { return pNode != p.pNode; } private: const Node *pNode; POSITION(Node *p) : pNode(p) { } }; protected: Node m_anchor; // Anchor node for the linked list. DWORD m_count; // Number of items in the list. Node* Front() const { return m_anchor.next; } Node* Back() const { return m_anchor.prev; } virtual HRESULT InsertAfter(Ptr item, Node *pBefore) { if (pBefore == NULL) { return E_POINTER; } // Do not allow NULL item pointers unless NULLABLE is true. if (!item && !NULLABLE) { return E_POINTER; } Node *pNode = new Node(item); if (pNode == NULL) { return E_OUTOFMEMORY; } Node *pAfter = pBefore->next; pBefore->next = pNode; pAfter->prev = pNode; pNode->prev = pBefore; pNode->next = pAfter; m_count++; return S_OK; } virtual HRESULT GetItem(const Node *pNode, Ptr* ppItem) { if (pNode == NULL || ppItem == NULL) { return E_POINTER; } *ppItem = pNode->item; if (*ppItem) { (*ppItem)->AddRef(); } return S_OK; } // RemoveItem: // Removes a node and optionally returns the item. // ppItem can be NULL. virtual HRESULT RemoveItem(Node *pNode, Ptr *ppItem) { if (pNode == NULL) { return E_POINTER; } assert(pNode != &m_anchor); // We should never try to remove the anchor node. if (pNode == &m_anchor) { return E_INVALIDARG; } Ptr item; // The next node's previous is this node's previous. pNode->next->prev = pNode->prev; // The previous node's next is this node's next. pNode->prev->next = pNode->next; item = pNode->item; if (ppItem) { *ppItem = item; if (*ppItem) { (*ppItem)->AddRef(); } } delete pNode; m_count--; return S_OK; } public: ComPtrListEx() { m_anchor.next = &m_anchor; m_anchor.prev = &m_anchor; m_count = 0; } virtual ~ComPtrListEx() { Clear(); } void Clear() { Node *n = m_anchor.next; // Delete the nodes while (n != &m_anchor) { if (n->item) { n->item->Release(); n->item = NULL; } Node *tmp = n->next; delete n; n = tmp; } // Reset the anchor to point at itself m_anchor.next = &m_anchor; m_anchor.prev = &m_anchor; m_count = 0; } // Insertion functions HRESULT InsertBack(Ptr item) { return InsertAfter(item, m_anchor.prev); } HRESULT InsertFront(Ptr item) { return InsertAfter(item, &m_anchor); } // RemoveBack: Removes the tail of the list and returns the value. // ppItem can be NULL if you don't want the item back. HRESULT RemoveBack(Ptr *ppItem) { if (IsEmpty()) { return E_FAIL; } else { return RemoveItem(Back(), ppItem); } } // RemoveFront: Removes the head of the list and returns the value. // ppItem can be NULL if you don't want the item back. HRESULT RemoveFront(Ptr *ppItem) { if (IsEmpty()) { return E_FAIL; } else { return RemoveItem(Front(), ppItem); } } // GetBack: Gets the tail item. HRESULT GetBack(Ptr *ppItem) { if (IsEmpty()) { return E_FAIL; } else { return GetItem(Back(), ppItem); } } // GetFront: Gets the front item. HRESULT GetFront(Ptr *ppItem) { if (IsEmpty()) { return E_FAIL; } else { return GetItem(Front(), ppItem); } } // GetCount: Returns the number of items in the list. DWORD GetCount() const { return m_count; } bool IsEmpty() const { return (GetCount() == 0); } // Enumerator functions POSITION FrontPosition() { if (IsEmpty()) { return POSITION(NULL); } else { return POSITION(Front()); } } POSITION EndPosition() const { return POSITION(); } HRESULT GetItemByPosition(POSITION pos, Ptr *ppItem) { if (pos.pNode) { return GetItem(pos.pNode, ppItem); } else { return E_FAIL; } } POSITION Next(const POSITION pos) { if (pos.pNode && (pos.pNode->next != &m_anchor)) { return POSITION(pos.pNode->next); } else { return POSITION(NULL); } } };