//----------------------------------------------------------------------------- // File: Linklist.h // Desc: Linked list class. // // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A // PARTICULAR PURPOSE. // // Copyright (C) Microsoft Corporation. All rights reserved. //----------------------------------------------------------------------------- #pragma once // Notes: // // The List class template implements a simple double-linked list. // It uses STL's copy semantics. // There are two versions of the Clear() method: // Clear(void) clears the list w/out cleaning up the object. // Clear(FN fn) takes a functor object that releases the objects, if they need cleanup. // The List class supports enumeration. Example of usage: // // List::POSIITON pos = list.GetFrontPosition(); // while (pos != list.GetEndPosition()) // { // T item; // hr = list.GetItemPos(&item); // pos = list.Next(pos); // } // The ComPtrList class template derives from List<> and implements a list of COM pointers. template struct NoOp { void operator()(T& t) { } }; template class List { protected: // Nodes in the linked list struct Node { Node *prev; Node *next; T item; Node() : prev(nullptr), next(nullptr) { } Node(T item) : prev(nullptr), next(nullptr) { this->item = item; } T Item() const { return item; } }; public: // Object for enumerating the list. class POSITION { friend class List; public: POSITION() : pNode(nullptr) { } 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(T item, Node *pBefore) { if (pBefore == nullptr) { return E_POINTER; } Node *pNode = new Node(item); if (pNode == nullptr) { 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, T* ppItem) { if (pNode == nullptr || ppItem == nullptr) { return E_POINTER; } *ppItem = pNode->item; return S_OK; } // RemoveItem: // Removes a node and optionally returns the item. // ppItem can be nullptr. virtual HRESULT RemoveItem(Node *pNode, T *ppItem) { if (pNode == nullptr) { return E_POINTER; } assert(pNode != &m_anchor); // We should never try to remove the anchor node. if (pNode == &m_anchor) { return E_INVALIDARG; } T 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; delete pNode; m_count--; if (ppItem) { *ppItem = item; } return S_OK; } public: List() { m_anchor.next = &m_anchor; m_anchor.prev = &m_anchor; m_count = 0; } virtual ~List() { Clear(); } // Insertion functions HRESULT InsertBack(T item) { return InsertAfter(item, m_anchor.prev); } HRESULT InsertFront(T item) { return InsertAfter(item, &m_anchor); } HRESULT InsertPos(POSITION pos, T item) { if (pos.pNode == nullptr) { return InsertBack(item); } return InsertAfter(item, pos.pNode->prev); } // RemoveBack: Removes the tail of the list and returns the value. // ppItem can be nullptr if you don't want the item back. (But the method does not release the item.) 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. // ppItem can be nullptr if you don't want the item back. (But the method does not release the item.) HRESULT RemoveFront(T *ppItem) { if (IsEmpty()) { return E_FAIL; } else { return RemoveItem(Front(), ppItem); } } // GetBack: Gets the tail item. HRESULT GetBack(T *ppItem) { if (IsEmpty()) { return E_FAIL; } else { return GetItem(Back(), ppItem); } } // GetFront: Gets the front item. 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; } // Clear: Clears the list. (Does not delete or release the list items.) virtual void Clear() { NoOp clearOp; Clear<>(clearOp); } // Enumerator functions POSITION FrontPosition() { if (IsEmpty()) { return POSITION(nullptr); } else { return POSITION(Front()); } } POSITION EndPosition() const { return POSITION(); } HRESULT GetItemPos(POSITION pos, T *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(nullptr); } } // Remove an item at a position. // The item is returns in ppItem, unless ppItem is nullptr. // NOTE: This method invalidates the POSITION object. HRESULT Remove(POSITION& pos, T *ppItem) { if (pos.pNode) { // Remove const-ness temporarily... Node *pNode = const_cast(pos.pNode); pos = POSITION(); return RemoveItem(pNode, ppItem); } else { return E_INVALIDARG; } } }; // 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. // example: ComPtrList // // NULLABLE: If true, client can insert nullptr pointers. This means GetItem can // succeed but return a nullptr pointer. By default, the list does not allow nullptr // pointers. template class ComPtrList : public List { public: typedef T* Ptr; void Clear() { ComAutoRelease car; List::Clear(car); } ~ComPtrList() { Clear(); } protected: HRESULT InsertAfter(Ptr item, Node *pBefore) { // Do not allow nullptr item pointers unless NULLABLE is true. if (item == nullptr && !NULLABLE) { return E_POINTER; } if (item) { item->AddRef(); } HRESULT hr = List::InsertAfter(item, pBefore); if (FAILED(hr) && item != nullptr) { item->Release(); } return hr; } HRESULT GetItem(const Node *pNode, Ptr* ppItem) { Ptr pItem = nullptr; // The base class gives us the pointer without AddRef'ing it. // If we return the pointer to the caller, we must AddRef(). HRESULT hr = List::GetItem(pNode, &pItem); if (SUCCEEDED(hr)) { assert(pItem || NULLABLE); if (pItem) { *ppItem = pItem; (*ppItem)->AddRef(); } } return hr; } HRESULT RemoveItem(Node *pNode, Ptr *ppItem) { // ppItem can be nullptr, but we need to get the // item so that we can release it. // If ppItem is not nullptr, we will AddRef it on the way out. Ptr pItem = nullptr; HRESULT hr = List::RemoveItem(pNode, &pItem); if (SUCCEEDED(hr)) { assert(pItem || NULLABLE); if (ppItem && pItem) { *ppItem = pItem; (*ppItem)->AddRef(); } if (pItem) { pItem->Release(); pItem = nullptr; } } return hr; } };