/** * \file * \brief IntrusiveList template class header * * \author Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info * * \par License * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not * distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef ESTD_INTRUSIVELIST_HPP_ #define ESTD_INTRUSIVELIST_HPP_ #include #include namespace estd { namespace internal { class IntrusiveListBase; } /** * \brief IntrusiveListNode class is the node that is needed for the object to be linked in IntrusiveList * * To some extent, this class can be considered to be a limited (raw) iterator. * * The object that wants to be linked in IntrusiveList must contain a variable of this type - one for each intrusive * list that will be used with object. */ class IntrusiveListNode { public: /// LinkAccessKey class is used to limit access to IntrusiveListNode::link() function - only /// internal::IntrusiveListBase can link nodes to the list class LinkAccessKey { friend class internal::IntrusiveListBase; /** * \brief LinkAccessKey's constructor */ constexpr LinkAccessKey() { } LinkAccessKey(const LinkAccessKey&) = delete; LinkAccessKey(LinkAccessKey&&) = delete; const LinkAccessKey& operator=(const LinkAccessKey&) = delete; LinkAccessKey& operator=(LinkAccessKey&&) = delete; }; /** * \brief IntrusiveListNode's constructor */ constexpr IntrusiveListNode() : nextNode_{this}, previousNode_{this} { } /** * \brief IntrusiveListNode's move constructor * * \param [in] other is a rvalue reference to IntrusiveListNode used as source of move construction */ IntrusiveListNode(IntrusiveListNode&& other) { if (other.isLinked() == false) { reset(); return; } nextNode_ = other.nextNode_; previousNode_ = other.previousNode_; nextNode_->previousNode_ = previousNode_->nextNode_ = this; other.reset(); } /** * \brief IntrusiveListNode's destructor * * Unlinks the node from the list. */ ~IntrusiveListNode() { unlink(); } /** * \return reference to next node on the list */ IntrusiveListNode& getNextNode() const { return *nextNode_; } /** * \return reference to previous node on the list */ IntrusiveListNode& getPreviousNode() const { return *previousNode_; } /** * \return true if the node is linked in some list, false otherwise */ bool isLinked() const { return nextNode_ != this; } /** * \brief Links the node in the list before \a position. * * \note Access to this function is restricted only to functions from internal::IntrusiveListBase class * * \param [in] position is a reference to node before which this node will be linked * \param [in] linkAccessKey is used to limit access to this function */ void link(IntrusiveListNode& position, LinkAccessKey) { unlink(); nextNode_ = &position; previousNode_ = position.previousNode_; position.previousNode_->nextNode_ = this; position.previousNode_ = this; } /** * \brief Swaps contents with another node. * * \param [in] other is a reference to IntrusiveListNode with which contents of this node will be swapped */ void swap(IntrusiveListNode& other) { const auto thisWasLinked = isLinked(); const auto otherWasLinked = other.isLinked(); if (thisWasLinked == true || otherWasLinked == true) { using std::swap; swap(nextNode_, other.nextNode_); swap(previousNode_, other.previousNode_); if (thisWasLinked == true) other.nextNode_->previousNode_ = other.previousNode_->nextNode_ = &other; else other.reset(); if (otherWasLinked == true) nextNode_->previousNode_ = previousNode_->nextNode_ = this; else reset(); } } /** * \brief Unlinks the node from the list. */ void unlink() { previousNode_->nextNode_ = nextNode_; nextNode_->previousNode_ = previousNode_; reset(); } IntrusiveListNode(const IntrusiveListNode&) = delete; const IntrusiveListNode& operator=(const IntrusiveListNode&) = delete; IntrusiveListNode& operator=(IntrusiveListNode&&) = delete; private: /** * \brief Resets the node to the same state as right after construction. */ void reset() { nextNode_ = this; previousNode_ = this; } /// reference to next node on the list IntrusiveListNode* nextNode_; /// reference to previous node on the list IntrusiveListNode* previousNode_; }; namespace internal { /** * \brief IntrusiveListBase class provides base functionalities for IntrusiveList class, but without any knowledge about * types * * This class tries to provide an interface similar to std::list. */ class IntrusiveListBase { public: /** * \brief IntrusiveListBase's constructor */ constexpr IntrusiveListBase() : rootNode_{} { } /** * \brief IntrusiveListBase's destructor * * Unlinks all nodes from the list. */ ~IntrusiveListBase() { clear(); } /** * \return reference to first node on the list */ IntrusiveListNode& begin() { return rootNode_.getNextNode(); } /** * \return const reference to first node on the list */ const IntrusiveListNode& begin() const { return rootNode_.getNextNode(); } /** * \return const reference to first node on the list */ const IntrusiveListNode& cbegin() const { return begin(); } /** * \return const reference to "one past the last" node on the list */ const IntrusiveListNode& cend() const { return end(); } /** * \brief Unlinks all nodes from the list. */ void clear() { while (empty() == false) pop_front(); } /** * \return true is the list is empty, false otherwise */ bool empty() const { return &begin() == &end(); } /** * \return reference to "one past the last" node on the list */ IntrusiveListNode& end() { return rootNode_; } /** * \return const reference to "one past the last" node on the list */ const IntrusiveListNode& end() const { return rootNode_; } /** * \brief Unlinks the last node from the list. */ void pop_back() { erase(end().getPreviousNode()); } /** * \brief Unlinks the first node from the list. */ void pop_front() { erase(begin()); } /** * \brief Links the node at the end of the list. * * \param [in] newNode is a reference to node that will be linked in the list */ void push_back(IntrusiveListNode& newNode) { insert(end(), newNode); } /** * \brief Links the node at the beginning of the list. * * \param [in] newNode is a reference to node that will be linked in the list */ void push_front(IntrusiveListNode& newNode) { insert(begin(), newNode); } /** * \brief Swaps contents with another list. * * \param [in] other is a reference to IntrusiveListBase with which contents of this list will be swapped */ void swap(IntrusiveListBase& other) { rootNode_.swap(other.rootNode_); } /** * \brief Unlinks the node at \a position from the list. * * \note No instance of the list is needed for this operation. * * \param [in] position is a reference to the node that will be unlinked from the list * * \return reference to the node that was following the node which was unlinked */ static IntrusiveListNode& erase(IntrusiveListNode& position) { auto& next = position.getNextNode(); position.unlink(); return next; } /** * \brief Links the node in the list before \a position. * * \note No instance of the list is needed for this operation. * * \param [in] position is a reference to node before which \a newNode will be linked * \param [in] newNode is a reference to node that will be linked in the list */ static void insert(IntrusiveListNode& position, IntrusiveListNode& newNode) { newNode.link(position, {}); } /** * \brief Transfers the node from one list to another list before \a position. * * \note No instance of any list is needed for this operation. * * \param [in] position is a reference to node before which \a splicedNode will be linked * \param [in] splicedNode is a reference to node that will be spliced from one list to another */ static void splice(IntrusiveListNode& position, IntrusiveListNode& splicedNode) { insert(position, splicedNode); } IntrusiveListBase(const IntrusiveListBase&) = delete; IntrusiveListBase(IntrusiveListBase&&) = default; const IntrusiveListBase& operator=(const IntrusiveListBase&) = delete; IntrusiveListBase& operator=(IntrusiveListBase&&) = delete; private: /// root node of the intrusive list IntrusiveListNode rootNode_; }; /** * \brief Swaps contents of two lists. * * \param [in] left is a reference to IntrusiveListBase with which contents of \a right will be swapped * \param [in] right is a reference to IntrusiveListBase with which contents of \a left will be swapped */ inline void swap(IntrusiveListBase& left, IntrusiveListBase& right) { left.swap(right); } } // namespace internal /** * \brief IntrusiveListIterator class is an iterator of elements on IntrusiveList. * * This class provides an interface similar to std::list::iterator. * * \tparam T is the type that has the IntrusiveListNode variable * \tparam NodePointer is a pointer-to-member to IntrusiveListNode variable in \a T * \tparam U is the type that will be stored on the list; it can be different from \a T, but must be implicitly * convertible to \a T (so usually a type derived from \a T); default - \a T; */ template class IntrusiveListIterator { public: /// difference type using difference_type = ptrdiff_t; /// category of the iterator using iterator_category = std::bidirectional_iterator_tag; /// pointer to object "pointed to" by the iterator using pointer = U*; /// reference to object "pointed to" by the iterator using reference = U&; /// value "pointed to" by the iterator using value_type = U; /** * \brief IntrusiveListIterator's constructor */ constexpr IntrusiveListIterator() : node_{} { } /** * \brief IntrusiveListIterator's constructor * * \param [in] node is a pointer to IntrusiveListNode of element that will be "pointed to" by the iterator */ constexpr explicit IntrusiveListIterator(IntrusiveListNode* const node) : node_{node} { } /** * \brief IntrusiveListIterator's constructor * * \param [in] element is a reference to element that will be "pointed to" by the iterator */ constexpr explicit IntrusiveListIterator(reference element) : node_{&(element.*NodePointer)} { static_assert(std::is_convertible::value == true, "U must be implicitly convertible to T!"); } /** * \brief IntrusiveListIterator's binary infix pointer member access operator * * \return pointer to object "pointed to" by the iterator */ pointer operator->() const { return getPointer(); } /** * \brief IntrusiveListIterator's unary prefix dereference operator * * \return reference to object "pointed to" by the iterator */ reference operator*() const { return *getPointer(); } /** * \brief IntrusiveListIterator's unary prefix increment operator * * \return reference to "this" iterator */ IntrusiveListIterator& operator++() { node_ = &node_->getNextNode(); return *this; } /** * \brief IntrusiveListIterator's unary postfix increment operator * * \return copy of "this" iterator before increment */ IntrusiveListIterator operator++(int) { const auto temporary = *this; node_ = &node_->getNextNode(); return temporary; } /** * \brief IntrusiveListIterator's unary prefix decrement operator * * \return reference to "this" iterator */ IntrusiveListIterator& operator--() { node_ = &node_->getPreviousNode(); return *this; } /** * \brief IntrusiveListIterator's unary postfix decrement operator * * \return copy of "this" iterator before decrement */ IntrusiveListIterator operator--(int) { const auto temporary = *this; node_ = &node_->getPreviousNode(); return temporary; } /** * \brief IntrusiveListIterator's "equal to" comparison operator * * \param [in] other is a const reference to IntrusiveListIterator on right-hand side of comparison operator * * \return true if both iterators are equal, false otherwise */ bool operator==(const IntrusiveListIterator& other) const { return node_ == other.node_; } private: /** * \brief Converts contained pointer to IntrusiveListNode to pointer to object that contains this node. * * \return pointer to object "pointed to" by the iterator */ pointer getPointer() const { static_assert(std::is_convertible::value == true, "U must be implicitly convertible to T!"); const auto offset = reinterpret_cast(&(static_cast(nullptr)->*NodePointer)); return reinterpret_cast(reinterpret_cast(node_) - offset); } /// pointer to IntrusiveListNode of the object "pointed to" by the iterator IntrusiveListNode* node_; }; /** * \brief IntrusiveListIterator's "not equal to" comparison operator * * \tparam T is the type that has the IntrusiveListNode variable * \tparam NodePointer is a pointer-to-member to IntrusiveListNode variable in \a T * \tparam U is the type that will be stored on the list; it can be different from \a T, but must be implicitly * convertible to \a T (so usually a type derived from \a T); default - \a T; * * \param [in] left is a const reference to IntrusiveListIterator on left-hand side of comparison operator * \param [in] right is a const reference to IntrusiveListIterator on right-hand side of comparison operator * * \return true if iterators are not equal, false otherwise */ template inline bool operator!=(const IntrusiveListIterator& left, const IntrusiveListIterator& right) { return (left == right) == false; } /** * \brief IntrusiveListConstIterator class is a const iterator of elements on IntrusiveList. * * This class provides an interface similar to std::list::const_iterator. * * \tparam T is the type that has the IntrusiveListNode variable * \tparam NodePointer is a const pointer-to-member to IntrusiveListNode variable in \a T * \tparam U is the type that will be stored on the list; it can be different from \a T, but must be implicitly * convertible to \a T (so usually a type derived from \a T); default - \a T; */ template class IntrusiveListConstIterator { public: /// difference type using difference_type = ptrdiff_t; /// category of the iterator using iterator_category = std::bidirectional_iterator_tag; /// pointer to object "pointed to" by the iterator using pointer = const U*; /// reference to object "pointed to" by the iterator using reference = const U&; /// value "pointed to" by the iterator using value_type = U; /** * \brief IntrusiveListConstIterator's constructor */ constexpr IntrusiveListConstIterator() : node_{} { } /** * \brief IntrusiveListConstIterator's constructor * * \param [in] node is a pointer to const IntrusiveListNode of element that will be "pointed to" by the iterator */ constexpr explicit IntrusiveListConstIterator(const IntrusiveListNode* const node) : node_{node} { } /** * \brief IntrusiveListConstIterator's constructor * * \param [in] element is a const reference to element that will be "pointed to" by the iterator */ constexpr explicit IntrusiveListConstIterator(reference element) : node_{&(element.*NodePointer)} { static_assert(std::is_convertible::value == true, "U must be implicitly convertible to T!"); } /** * \brief IntrusiveListConstIterator's constructor * * Converts non-const iterator (IntrusiveListIterator) to const iterator (IntrusiveListConstIterator). * * \tparam NonConstNodePointer is a non-const version of \a NodePointer * * \param [in] iterator is a const reference to non-const iterator (IntrusiveListIterator) */ template constexpr IntrusiveListConstIterator(const IntrusiveListIterator& iterator) : IntrusiveListConstIterator{*iterator} { } /** * \brief IntrusiveListConstIterator's binary infix pointer member access operator * * \return pointer to object "pointed to" by the iterator */ pointer operator->() const { return getPointer(); } /** * \brief IntrusiveListConstIterator's unary prefix dereference operator * * \return reference to object "pointed to" by the iterator */ reference operator*() const { return *getPointer(); } /** * \brief IntrusiveListConstIterator's unary prefix increment operator * * \return reference to "this" iterator */ IntrusiveListConstIterator& operator++() { node_ = &node_->getNextNode(); return *this; } /** * \brief IntrusiveListConstIterator's unary postfix increment operator * * \return copy of "this" iterator before increment */ IntrusiveListConstIterator operator++(int) { const auto temporary = *this; node_ = &node_->getNextNode(); return temporary; } /** * \brief IntrusiveListConstIterator's unary prefix decrement operator * * \return reference to "this" iterator */ IntrusiveListConstIterator& operator--() { node_ = &node_->getPreviousNode(); return *this; } /** * \brief IntrusiveListConstIterator's unary postfix decrement operator * * \return copy of "this" iterator before decrement */ IntrusiveListConstIterator operator--(int) { const auto temporary = *this; node_ = &node_->getPreviousNode(); return temporary; } /** * \brief IntrusiveListConstIterator's "equal to" comparison operator * * \param [in] other is a const reference to IntrusiveListConstIterator on right-hand side of comparison operator * * \return true if both iterators are equal, false otherwise */ bool operator==(const IntrusiveListConstIterator& other) const { return node_ == other.node_; } private: /** * \brief Converts contained pointer to IntrusiveListNode to pointer to object that contains this node. * * \return pointer to object "pointed to" by the iterator */ pointer getPointer() const { static_assert(std::is_convertible::value == true, "U must be implicitly convertible to T!"); const auto offset = reinterpret_cast(&(static_cast(nullptr)->*NodePointer)); return reinterpret_cast(reinterpret_cast(node_) - offset); } /// pointer to const IntrusiveListNode of the object "pointed to" by the iterator const IntrusiveListNode* node_; }; /** * \brief IntrusiveListConstIterator's "not equal to" comparison operator * * \tparam T is the type that has the IntrusiveListNode variable * \tparam NodePointer is a const pointer-to-member to IntrusiveListNode variable in \a T * \tparam U is the type that will be stored on the list; it can be different from \a T, but must be implicitly * convertible to \a T (so usually a type derived from \a T); default - \a T; * * \param [in] left is a const reference to IntrusiveListConstIterator on left-hand side of comparison operator * \param [in] right is a const reference to IntrusiveListConstIterator on right-hand side of comparison operator * * \return true if iterators are not equal, false otherwise */ template inline bool operator!=(const IntrusiveListConstIterator& left, const IntrusiveListConstIterator& right) { return (left == right) == false; } /** * \brief "Equal to" comparison operator for IntrusiveListIterator and IntrusiveListConstIterator * * \tparam T is the type that has the IntrusiveListNode variable * \tparam NodePointer is a pointer-to-member to IntrusiveListNode variable in \a T * \tparam ConstNodePointer is a const pointer-to-member to IntrusiveListNode variable in \a T * \tparam U is the type that will be stored on the list; it can be different from \a T, but must be implicitly * convertible to \a T (so usually a type derived from \a T); default - \a T; * * \param [in] left is a const reference to IntrusiveListIterator on left-hand side of comparison operator * \param [in] right is a const reference to IntrusiveListConstIterator on right-hand side of comparison operator * * \return true if both iterators are equal, false otherwise */ template inline bool operator==(const IntrusiveListIterator& left, const IntrusiveListConstIterator& right) { return decltype(right){left} == right; } /** * \brief "Not equal to" comparison operator for IntrusiveListIterator and IntrusiveListConstIterator * * \tparam T is the type that has the IntrusiveListNode variable * \tparam NodePointer is a pointer-to-member to IntrusiveListNode variable in \a T * \tparam ConstNodePointer is a const pointer-to-member to IntrusiveListNode variable in \a T * \tparam U is the type that will be stored on the list; it can be different from \a T, but must be implicitly * convertible to \a T (so usually a type derived from \a T); default - \a T; * * \param [in] left is a const reference to IntrusiveListIterator on left-hand side of comparison operator * \param [in] right is a const reference to IntrusiveListConstIterator on right-hand side of comparison operator * * \return true if iterators are not equal, false otherwise */ template inline bool operator!=(const IntrusiveListIterator& left, const IntrusiveListConstIterator& right) { return (left == right) == false; } /** * \brief "Not equal to" comparison operator for IntrusiveListConstIterator and IntrusiveListIterator * * \tparam T is the type that has the IntrusiveListNode variable * \tparam NodePointer is a pointer-to-member to IntrusiveListNode variable in \a T * \tparam ConstNodePointer is a const pointer-to-member to IntrusiveListNode variable in \a T * \tparam U is the type that will be stored on the list; it can be different from \a T, but must be implicitly * convertible to \a T (so usually a type derived from \a T); default - \a T; * * \param [in] left is a const reference to IntrusiveListConstIterator on left-hand side of comparison operator * \param [in] right is a const reference to IntrusiveListIterator on right-hand side of comparison operator * * \return true if iterators are not equal, false otherwise */ template inline bool operator!=(const IntrusiveListConstIterator& left, const IntrusiveListIterator& right) { return right != left; } /** * \brief IntrusiveList class is an intrusive circular doubly linked list. * * This class tries to provide an interface similar to std::list. * * \tparam T is the type that has the IntrusiveListNode variable * \tparam NodePointer is a pointer-to-member to IntrusiveListNode variable in \a T * \tparam U is the type that will be stored on the list; it can be different from \a T, but must be implicitly * convertible to \a T (so usually a type derived from \a T); default - \a T; using different type than \a T can be used * to break circular dependencies, because \a T must be fully defined to instantiate this class, but it is enough to * forward declare \a U - it only needs to be fully defined to use member functions */ template class IntrusiveList { public: /// const iterator of elements on the list using const_iterator = IntrusiveListConstIterator; /// const reverse iterator of elements on the list using const_reverse_iterator = std::reverse_iterator; /// const pointer to value linked in the list using const_pointer = const U*; /// const reference to value linked in the list using const_reference = const U&; /// iterator of elements on the list using iterator = IntrusiveListIterator; /// reverse iterator of elements on the list using reverse_iterator = std::reverse_iterator; /// pointer to value linked in the list using pointer = U*; /// reference to value linked in the list using reference = U&; /// value linked in the list using value_type = U; /** * \brief IntrusiveList's constructor */ constexpr IntrusiveList() : intrusiveListBase_{} { } /** * \return reference to last element on the list */ reference back() { return *--end(); } /** * \return const reference to last element on the list */ const_reference back() const { return *--end(); } /** * \return iterator of first element on the list */ iterator begin() { return iterator{&intrusiveListBase_.begin()}; } /** * \return const iterator of first element on the list */ const_iterator begin() const { return const_iterator{&intrusiveListBase_.begin()}; } /** * \return const iterator of first element on the list */ const_iterator cbegin() const { return begin(); } /** * \return const iterator of "one past the last" element on the list */ const_iterator cend() const { return end(); } /** * \brief Unlinks all elements from the list. */ void clear() { intrusiveListBase_.clear(); } /** * \return true is the list is empty, false otherwise */ bool empty() const { return intrusiveListBase_.empty(); } /** * \return iterator of "one past the last" element on the list */ iterator end() { return iterator{&intrusiveListBase_.end()}; } /** * \return const iterator of "one past the last" element on the list */ const_iterator end() const { return const_iterator{&intrusiveListBase_.end()}; } /** * \return reference to first element on the list */ reference front() { return *begin(); } /** * \return const reference to first element on the list */ const_reference front() const { return *begin(); } /** * \brief Unlinks the last element from the list. */ void pop_back() { erase(--end()); } /** * \brief Unlinks the first element from the list. */ void pop_front() { erase(begin()); } /** * \brief Links the element at the end of the list. * * \param [in] newElement is a reference to the element that will be linked in the list */ void push_back(reference newElement) { insert(end(), newElement); } /** * \brief Links the element at the beginning of the list. * * \param [in] newElement is a reference to the element that will be linked in the list */ void push_front(reference newElement) { insert(begin(), newElement); } /** * \brief Swaps contents with another list. * * \param [in] other is a reference to IntrusiveList with which contents of this list will be swapped */ void swap(IntrusiveList& other) { intrusiveListBase_.swap(other.intrusiveListBase_); } /** * \brief Unlinks the element at \a position from the list. * * \note No instance of the list is needed for this operation. * * \param [in] position is an iterator of the element that will be unlinked from the list * * \return iterator of the element that was following the element which was unlinked */ static iterator erase(const iterator position) { auto& positionNode = (*position).*NodePointer; auto& nextNode = internal::IntrusiveListBase::erase(positionNode); return iterator{&nextNode}; } /** * \brief Links the element in the list before \a position. * * \note No instance of the list is needed for this operation. * * \param [in] position is an iterator of the element before which \a newNode will be linked * \param [in] newElement is a reference to the element that will be linked in the list * * \return iterator of \a newElement */ static iterator insert(const iterator position, reference newElement) { static_assert(std::is_convertible::value == true, "U must be implicitly convertible to T!"); auto& positionNode = (*position).*NodePointer; auto& newElementNode = newElement.*NodePointer; internal::IntrusiveListBase::insert(positionNode, newElementNode); return iterator{&newElementNode}; } /** * \brief Transfers the element from one list to another list before \a position. * * \note No instance of any list is needed for this operation. * * \param [in] position is an iterator of the element before which \a splicedElement will be linked * \param [in] splicedElement is an iterator of the element that will be spliced from one list to another */ static void splice(const iterator position, const iterator splicedElement) { auto& positionNode = (*position).*NodePointer; auto& splicedElementNode = (*splicedElement).*NodePointer; internal::IntrusiveListBase::splice(positionNode, splicedElementNode); } IntrusiveList(const IntrusiveList&) = delete; IntrusiveList(IntrusiveList&&) = default; const IntrusiveList& operator=(const IntrusiveList&) = delete; IntrusiveList& operator=(IntrusiveList&&) = delete; private: /// internal IntrusiveListBase object internal::IntrusiveListBase intrusiveListBase_; }; /** * \brief Swaps contents of two lists. * * \tparam T is the type that has the IntrusiveListNode variable * \tparam NodePointer is a pointer-to-member to IntrusiveListNode variable in \a T * \tparam U is the type that will be stored on the list; it can be different from \a T, but must be implicitly * convertible to \a T (so usually a type derived from \a T); default - \a T; * * \param [in] left is a reference to IntrusiveList with which contents of \a right will be swapped * \param [in] right is a reference to IntrusiveList with which contents of \a left will be swapped */ template inline void swap(IntrusiveList& left, IntrusiveList& right) { left.swap(right); } } // namespace estd #endif // ESTD_INTRUSIVELIST_HPP_