diff --git a/doc/developer-guide/internal-libraries/intrusive-list.en.rst b/doc/developer-guide/internal-libraries/intrusive-list.en.rst index d5f79f31b7a..3a33d55b7dc 100644 --- a/doc/developer-guide/internal-libraries/intrusive-list.en.rst +++ b/doc/developer-guide/internal-libraries/intrusive-list.en.rst @@ -134,7 +134,7 @@ Examples In this example the goal is to have a list of :code:`Message` objects. First the class is declared along with the internal linkage support. -.. literalinclude:: ../../../src/tscore/unit_tests/test_IntrusiveDList.cc +.. literalinclude:: ../../../src/tscpp/util/unit_tests/test_IntrusiveDList.cc :lines: 38-63 The struct :code:`Linkage` is used both to provide the descriptor to :class:`IntrusiveDList` and to @@ -144,35 +144,35 @@ used only by a specific container class (:code:`Container`) the struct is made p The implementation of the link accessor methods. -.. literalinclude:: ../../../src/tscore/unit_tests/test_IntrusiveDList.cc +.. literalinclude:: ../../../src/tscpp/util/unit_tests/test_IntrusiveDList.cc :lines: 65-74 A method to check if the message is in a list. -.. literalinclude:: ../../../src/tscore/unit_tests/test_IntrusiveDList.cc +.. literalinclude:: ../../../src/tscpp/util/unit_tests/test_IntrusiveDList.cc :lines: 76-80 The container class for the messages could be implemented as -.. literalinclude:: ../../../src/tscore/unit_tests/test_IntrusiveDList.cc +.. literalinclude:: ../../../src/tscpp/util/unit_tests/test_IntrusiveDList.cc :lines: 82-99 The :code:`debug` method takes a format string (:arg:`fmt`) and an arbitrary set of arguments, formats the arguments in to the string, and adds the new message to the list. -.. literalinclude:: ../../../src/tscore/unit_tests/test_IntrusiveDList.cc +.. literalinclude:: ../../../src/tscpp/util/unit_tests/test_IntrusiveDList.cc :lines: 122-131 The :code:`print` method demonstrates the use of the range :code:`for` loop on a list. -.. literalinclude:: ../../../src/tscore/unit_tests/test_IntrusiveDList.cc +.. literalinclude:: ../../../src/tscpp/util/unit_tests/test_IntrusiveDList.cc :lines: 142-148 The maximum severity level can also be computed even more easily using :code:`std::max_element`. This find the element with the maximum severity and returns that severity, or :code:`LVL_DEBUG` if no element is found (which happens if the list is empty). -.. literalinclude:: ../../../src/tscore/unit_tests/test_IntrusiveDList.cc +.. literalinclude:: ../../../src/tscpp/util/unit_tests/test_IntrusiveDList.cc :lines: 134-140 Other methods for the various severity levels would be implemented in a similar fashion. Because the @@ -183,20 +183,20 @@ risky. One approach, illustrated here, is to use :func:`IntrusiveDList::take_hea element before destroying it. Another option is to allocation the elements in a :class:`MemArena` to avoid the need for any explicit cleanup. -.. literalinclude:: ../../../src/tscore/unit_tests/test_IntrusiveDList.cc +.. literalinclude:: ../../../src/tscpp/util/unit_tests/test_IntrusiveDList.cc :lines: 106-114 In some cases the elements of the list are subclasses and the links are declared in a super class and are therefore of the super class type. For instance, in the unit test a class :code:`Thing` is defined for testing. -.. literalinclude:: ../../../src/tscore/unit_tests/test_IntrusiveDList.cc +.. literalinclude:: ../../../src/tscpp/util/unit_tests/test_IntrusiveDList.cc :lines: 159 Later on, to validate use on a subclass, :code:`PrivateThing` is defined as a subclass of :code:`Thing`. -.. literalinclude:: ../../../src/tscore/unit_tests/test_IntrusiveDList.cc +.. literalinclude:: ../../../src/tscpp/util/unit_tests/test_IntrusiveDList.cc :lines: 181 However, the link members :code:`_next` and :code:`_prev` are of type :code:`Thing*` but the @@ -205,7 +205,7 @@ descriptor for a list of :code:`PrivateThing` must have link accessors that retu :code:`ts::ptr_ref_cast` that converts a member of type :code:`T*` to a reference to a pointer to :code:`X`, e.g. :code:`X*&`. This is used in the setup for testing :code:`PrivateThing`. -.. literalinclude:: ../../../src/tscore/unit_tests/test_IntrusiveDList.cc +.. literalinclude:: ../../../src/tscpp/util/unit_tests/test_IntrusiveDList.cc :lines: 190-199 While this can be done directly with :code:`reinterpret_cast<>`, use of :code:`ts::ptr_cast` avoids diff --git a/include/tscore/IntrusiveHashMap.h b/include/tscore/IntrusiveHashMap.h index 06c50305dee..c787e4b358b 100644 --- a/include/tscore/IntrusiveHashMap.h +++ b/include/tscore/IntrusiveHashMap.h @@ -26,7 +26,7 @@ #include #include #include -#include "tscore/IntrusiveDList.h" +#include "tscpp/util/IntrusiveDList.h" /** Intrusive Hash Table. @@ -105,11 +105,11 @@ template class IntrusiveHashMap * All table elements are in this list. The buckets reference their starting element in the list, or nothing if * no elements are in that bucket. */ - using List = IntrusiveDList; + using List = ts::IntrusiveDList; /// A bucket for the hash map. struct Bucket { - /// Support for IntrusiveDList, definitions and link storage. + /// Support for ts::IntrusiveDList, definitions and link storage. struct Linkage { static Bucket *&next_ptr(Bucket *b); ///< Access next pointer. static Bucket *&prev_ptr(Bucket *b); ///< Access prev pointer. @@ -294,7 +294,7 @@ template class IntrusiveHashMap Table _table; ///< Array of buckets. /// List of non-empty buckets. - IntrusiveDList _active_buckets; + ts::IntrusiveDList _active_buckets; Bucket *bucket_for(key_type key); diff --git a/include/tscore/IpMap.h b/include/tscore/IpMap.h index fc95ebb1ccb..34ec5dc3175 100644 --- a/include/tscore/IpMap.h +++ b/include/tscore/IpMap.h @@ -27,7 +27,7 @@ #include "tscore/ink_defs.h" #include "tscore/RbTree.h" #include "tscore/ink_inet.h" -#include "tscore/IntrusiveDList.h" +#include "tscpp/util/IntrusiveDList.h" #include "tscore/ink_assert.h" namespace ts diff --git a/include/tscore/IntrusiveDList.h b/include/tscpp/util/IntrusiveDList.h similarity index 60% rename from include/tscore/IntrusiveDList.h rename to include/tscpp/util/IntrusiveDList.h index 2db6415bcb0..eae1f0e9186 100644 --- a/include/tscore/IntrusiveDList.h +++ b/include/tscpp/util/IntrusiveDList.h @@ -2,73 +2,66 @@ Intrusive double linked list container. - This provide support for a doubly linked list container for an - arbitrary class that uses the class directly and not wrapped. It - requires the class to provide the list pointers. + This provides support for a doubly linked list container. Items in the list must provide links + inside the class and accessor functions for those links. @note This is a header only library. - @note Due to bugs in either the C++ standard or gcc (or both), the - link members @b must be declared in the class used for the - list. If they are declared in a super class you will get "could - not convert template argument" errors, even though it should - work. This is because @c &T::m is of type @c S::* if @c S is a - super class of @c T and @c m is declared in @c S. My view is that - if I write "&T::m" I want a "T::*" and the compiler shouldn't go - rummaging through the class hierarchy for some other type. For - MSVC you can @c static_cast the template arguments as a - workaround, but not in gcc. - @section license License - Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you under the Apache License, Version 2.0 (the - "License"); you may not use this file except in compliance - with the License. You may obtain a copy of the License at + Licensed to the Apache Software Foundation (ASF) under one or more contributor license + agreements. See the NOTICE file distributed with this work for additional information regarding + copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with the License. You may + obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and + Unless required by applicable law or agreed to in writing, software distributed under the + License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + express or implied. See the License for the specific language governing permissions and limitations under the License. */ #pragma once -/// FreeBSD doesn't like just declaring the tag struct we need so we have to include the file. +/// Clang doesn't like just declaring the tag struct we need so we have to include the file. #include #include +namespace ts +{ /** Intrusive doubly linked list container. - This holds items in a doubly linked list using links in the items. Items are placed in the list by changing the - pointers. An item can be in only one list for a set of links, but an item can contain multiple sets of links. This - requires different specializations of this template because link access is part of the type specification. Memory - for items is not managed by this class - instances must be allocated and released elsewhere. In particular removing - an item from the list does not destruct or free the item. + This holds items in a doubly linked list using links in the items. Items are placed in the list + by changing the pointers. An item can be in only one list for a set of links, but an item can + contain multiple sets of links. This requires different specializations of this template because + link access is part of the type specification. Memory for items is not managed by this class - + instances must be allocated and released elsewhere. In particular removing an item from the list + does not destruct or free the item. - Access to the links is described by a linkage class which is required to contain the following members: + Access to the links is described by a linkage class which is required to contain the following + members: - The static method @c next_ptr which returns a reference to the pointer to the next item. - The static method @c prev_ptr which returns a reference to the pointer to the previous item. - The pointer methods take a single argument of @c Item* and must return a reference to a pointer instance. This - type is deduced from the methods and is not explicitly specified. It must be cheaply copyable and stateless. + The pointer methods take a single argument of @c Item* and must return a reference to a pointer + instance. This type is deduced from the methods and is not explicitly specified. It must be + cheaply copyable and stateless. + + It is the responsibility of the item class to initialize the link pointers. When an item is + removed from the list the link pointers are set to @c nullptr. An example declaration woudl be @code // Item in the list. struct Thing { - Thing* _next; - Thing* _prev; + Thing* _next {nullptr}; + Thing* _prev {nullptr}; Data _payload; // Linkage descriptor. @@ -78,12 +71,14 @@ }; }; - using ThingList = IntrusiveDList; + using ThingList = ts::IntrusiveDList; @endcode - Element access is done by using either STL style iteration, or direct access to the member pointers. A client can - have its own mechanism for getting an element to start, or use the @c head and/or @c tail methods to get the - first and last elements in the list respectively. Note if the list is empty then @c Linkage::NIL will be returned. + Item access is done by using either STL style iteration, or direct access to the member + pointers. A client can have its own mechanism for getting an element to start, or use the @c + head and/or @c tail methods to get the first and last elements in the list respectively. Note if + the list is empty then @c nullptr will be returned. There are simple and fast conversions + between item pointers and iterators. */ template class IntrusiveDList @@ -95,8 +90,7 @@ template class IntrusiveDList /// The list item type. using value_type = typename std::remove_pointer::type>::type; - /** Const iterator for the list. - */ + /// Const iterator. class const_iterator { using self_type = const_iterator; ///< Self reference type. @@ -163,8 +157,7 @@ template class IntrusiveDList const_iterator(const list_type *list, value_type *v); }; - /** Iterator for the list. - */ + /// Iterator for the list. class iterator : public const_iterator { using self_type = iterator; ///< Self reference type. @@ -212,7 +205,7 @@ template class IntrusiveDList value_type *operator->() const; /// Convenience conversion to pointer type - /// Because of how this list is normally used, being able to pass an iterator as a pointer is quite convienent. + /// Because of how this list is normally used, being able to pass an iterator as a pointer is quite convenient. /// If the iterator isn't valid, it converts to @c nullptr. operator value_type *() const; @@ -221,13 +214,24 @@ template class IntrusiveDList iterator(list_type *list, value_type *v); }; + /// Construct to empty list. + IntrusiveDList() = default; + + /// Move list to @a this and leave @a that empty. + IntrusiveDList(self_type &&that); + + /// No copy assignment because items can't be in two lists and can't copy items. + self_type &operator=(const self_type &that) = delete; + /// Move @a that to @a this. + self_type &operator=(self_type &&that); + /// Empty check. /// @return @c true if the list is empty. bool empty() const; /// Presence check (linear time). /// @return @c true if @a v is in the list, @c false if not. - bool contains(value_type *v) const; + bool contains(const value_type *v) const; /// Add @a elt as the first element in the list. /// @return This container. @@ -300,8 +304,9 @@ template class IntrusiveDList /** Get an iterator for the item @a v. * - * It is the responsibility of the caller that @a v is in the list. The purpose is to make iteration starting - * at a specific element easier (i.e. all of the link manipulation and checking is done by the iterator). + * It is the responsibility of the caller that @a v is in the list. The purpose is to make + * iteration starting at a specific element easier (i.e. all of the link manipulation and checking + * is done by the iterator). * * @return An @c iterator that refers to @a v. */ @@ -310,9 +315,16 @@ template class IntrusiveDList /// Get the first element. value_type *head(); + const value_type *head() const; /// Get the last element. value_type *tail(); + const value_type *tail() const; + + /** Apply a functor to every element in the list. + * This iterates over the list correctly even if the functor destroys or removes elements. + */ + template self_type &apply(F &&f); protected: value_type *_head{nullptr}; ///< First element in list. @@ -320,9 +332,50 @@ template class IntrusiveDList size_t _count{0}; ///< # of elements in list. }; -namespace ts +/** Utility class to provide intrusive links. + * + * @tparam T Class to link. + * + * The normal use is to declare this as a member to provide the links and the linkage functions. + * @code + * class Thing { + * // blah blah + * Thing* _next{nullptr}; + * Thing* _prev{nullptr}; + * using Linkage = ts::IntrusiveLinkage; + * }; + * using ThingList = ts::ts::IntrusiveDList; + * @endcode + * The template will default to the names '_next' and '_prev' therefore in the example it could + * have been done as + * @code + * using Linkage = ts::IntrusiveLinkage; + * @endcode + */ +template struct IntrusiveLinkage { + static T *&next_ptr(T *thing); ///< Retrieve reference to next pointer. + static T *&prev_ptr(T *thing); ///< Retrive reference to previous pointer. +}; + +template +T *& +IntrusiveLinkage::next_ptr(T *thing) +{ + return thing->*NEXT; +} +template +T *& +IntrusiveLinkage::prev_ptr(T *thing) { + return thing->*PREV; +} + /** Utility cast to change the underlying type of a pointer reference. + * + * @tparam T The resulting pointer reference type. + * @tparam P The starting pointer reference type. + * @param p A reference to pointer to @a P. + * @return A reference to the same pointer memory of type @c T*&. * * This changes a reference to a pointer to @a P to a reference to a pointer to @a T. This is useful * for intrusive links that are inherited. For instance @@ -335,16 +388,13 @@ namespace ts * To make @c BetterThing work with an intrusive container without making new link members, * * @code - * static BetterThing*& next_ptr(BetterThing* bt) { return ts::ptr_ref_cast(_next); } + * static BetterThing*& next_ptr(BetterThing* bt) { + * return ts::ptr_ref_cast(_next); + * } * @endcode * * This is both convenient and gets around aliasing warnings from the compiler that can arise from * using @c reinterpret_cast. - * - * @tparam T The resulting pointer reference type. - * @tparam P The starting pointer reference type. - * @param p A reference to pointer to @a P. - * @return A reference to the same pointer memory of type @c T*&. */ template T *& @@ -357,25 +407,23 @@ ptr_ref_cast(P *&p) return *(u._t); }; -} // namespace ts - // --- Implementation --- -template IntrusiveDList::const_iterator::const_iterator() {} +template ts::IntrusiveDList::const_iterator::const_iterator() {} template -IntrusiveDList::const_iterator::const_iterator(const list_type *list, value_type *v) +ts::IntrusiveDList::const_iterator::const_iterator(const list_type *list, value_type *v) : _list(const_cast(list)), _v(const_cast(v)) { } -template IntrusiveDList::iterator::iterator() {} +template ts::IntrusiveDList::iterator::iterator() {} -template IntrusiveDList::iterator::iterator(IntrusiveDList *list, value_type *v) : super_type(list, v) {} +template ts::IntrusiveDList::iterator::iterator(IntrusiveDList *list, value_type *v) : super_type(list, v) {} template auto -IntrusiveDList::const_iterator::operator++() -> self_type & +ts::IntrusiveDList::const_iterator::operator++() -> self_type & { _v = L::next_ptr(_v); return *this; @@ -383,7 +431,7 @@ IntrusiveDList::const_iterator::operator++() -> self_type & template auto -IntrusiveDList::iterator::operator++() -> self_type & +ts::IntrusiveDList::iterator::operator++() -> self_type & { this->super_type::operator++(); return *this; @@ -391,7 +439,7 @@ IntrusiveDList::iterator::operator++() -> self_type & template auto -IntrusiveDList::const_iterator::operator++(int) -> self_type +ts::IntrusiveDList::const_iterator::operator++(int) -> self_type { self_type tmp(*this); ++*this; @@ -400,7 +448,7 @@ IntrusiveDList::const_iterator::operator++(int) -> self_type template auto -IntrusiveDList::iterator::operator++(int) -> self_type +ts::IntrusiveDList::iterator::operator++(int) -> self_type { self_type tmp(*this); ++*this; @@ -409,7 +457,7 @@ IntrusiveDList::iterator::operator++(int) -> self_type template auto -IntrusiveDList::const_iterator::operator--() -> self_type & +ts::IntrusiveDList::const_iterator::operator--() -> self_type & { if (_v) { _v = L::prev_ptr(_v); @@ -421,7 +469,7 @@ IntrusiveDList::const_iterator::operator--() -> self_type & template auto -IntrusiveDList::iterator::operator--() -> self_type & +ts::IntrusiveDList::iterator::operator--() -> self_type & { this->super_type::operator--(); return *this; @@ -429,7 +477,7 @@ IntrusiveDList::iterator::operator--() -> self_type & template auto -IntrusiveDList::const_iterator::operator--(int) -> self_type +ts::IntrusiveDList::const_iterator::operator--(int) -> self_type { self_type tmp(*this); --*this; @@ -438,53 +486,61 @@ IntrusiveDList::const_iterator::operator--(int) -> self_type template auto -IntrusiveDList::iterator::operator--(int) -> self_type +ts::IntrusiveDList::iterator::operator--(int) -> self_type { self_type tmp(*this); --*this; return tmp; } -template auto IntrusiveDList::const_iterator::operator-> () const -> value_type * +template auto ts::IntrusiveDList::const_iterator::operator-> () const -> value_type * { return _v; } -template auto IntrusiveDList::iterator::operator-> () const -> value_type * +template auto ts::IntrusiveDList::iterator::operator-> () const -> value_type * { return super_type::_v; } -template IntrusiveDList::const_iterator::operator value_type *() const +template ts::IntrusiveDList::const_iterator::operator value_type *() const { return _v; } -template auto IntrusiveDList::const_iterator::operator*() const -> value_type & +template auto ts::IntrusiveDList::const_iterator::operator*() const -> value_type & { return *_v; } -template auto IntrusiveDList::iterator::operator*() const -> value_type & +template auto ts::IntrusiveDList::iterator::operator*() const -> value_type & { return *super_type::_v; } -template IntrusiveDList::iterator::operator value_type *() const +template ts::IntrusiveDList::iterator::operator value_type *() const { return super_type::_v; } +/// --- Main class + +template +ts::IntrusiveDList::IntrusiveDList(self_type &&that) : _head(that._head), _tail(that._tail), _count(that._count) +{ + that.clear(); +} + template bool -IntrusiveDList::empty() const +ts::IntrusiveDList::empty() const { return _head == nullptr; } template bool -IntrusiveDList::contains(value_type *v) const +ts::IntrusiveDList::contains(const value_type *v) const { for (auto thing = _head; thing; thing = L::next_ptr(thing)) { if (thing == v) @@ -495,21 +551,21 @@ IntrusiveDList::contains(value_type *v) const template bool -IntrusiveDList::const_iterator::operator==(self_type const &that) const +ts::IntrusiveDList::const_iterator::operator==(self_type const &that) const { return this->_v == that._v; } template bool -IntrusiveDList::const_iterator::operator!=(self_type const &that) const +ts::IntrusiveDList::const_iterator::operator!=(self_type const &that) const { return this->_v != that._v; } template auto -IntrusiveDList::prepend(value_type *v) -> self_type & +ts::IntrusiveDList::prepend(value_type *v) -> self_type & { L::prev_ptr(v) = nullptr; if (nullptr != (L::next_ptr(v) = _head)) { @@ -524,7 +580,7 @@ IntrusiveDList::prepend(value_type *v) -> self_type & template auto -IntrusiveDList::append(value_type *v) -> self_type & +ts::IntrusiveDList::append(value_type *v) -> self_type & { L::next_ptr(v) = nullptr; if (nullptr != (L::prev_ptr(v) = _tail)) { @@ -539,7 +595,7 @@ IntrusiveDList::append(value_type *v) -> self_type & template auto -IntrusiveDList::take_head() -> value_type * +ts::IntrusiveDList::take_head() -> value_type * { value_type *zret = _head; if (_head) { @@ -556,7 +612,7 @@ IntrusiveDList::take_head() -> value_type * template auto -IntrusiveDList::take_tail() -> value_type * +ts::IntrusiveDList::take_tail() -> value_type * { value_type *zret = _tail; if (_tail) { @@ -573,7 +629,7 @@ IntrusiveDList::take_tail() -> value_type * template auto -IntrusiveDList::insert_after(value_type *target, value_type *v) -> self_type & +ts::IntrusiveDList::insert_after(value_type *target, value_type *v) -> self_type & { if (target) { if (nullptr != (L::next_ptr(v) = L::next_ptr(target))) { @@ -593,14 +649,14 @@ IntrusiveDList::insert_after(value_type *target, value_type *v) -> self_type template auto -IntrusiveDList::insert_after(iterator const &target, value_type *v) -> self_type & +ts::IntrusiveDList::insert_after(iterator const &target, value_type *v) -> self_type & { return this->insert_after(target._v, v); } template auto -IntrusiveDList::insert_before(value_type *target, value_type *v) -> self_type & +ts::IntrusiveDList::insert_before(value_type *target, value_type *v) -> self_type & { if (target) { if (nullptr != (L::prev_ptr(v) = L::prev_ptr(target))) { @@ -620,14 +676,14 @@ IntrusiveDList::insert_before(value_type *target, value_type *v) -> self_type template auto -IntrusiveDList::insert_before(iterator const &target, value_type *v) -> self_type & +ts::IntrusiveDList::insert_before(iterator const &target, value_type *v) -> self_type & { return this->insert_before(target._v, v); } template auto -IntrusiveDList::erase(value_type *v) -> value_type * +ts::IntrusiveDList::erase(value_type *v) -> value_type * { value_type *zret{nullptr}; @@ -652,14 +708,14 @@ IntrusiveDList::erase(value_type *v) -> value_type * template auto -IntrusiveDList::erase(const iterator &loc) -> iterator +ts::IntrusiveDList::erase(const iterator &loc) -> iterator { return this->iterator_for(this->erase(loc._v)); }; template auto -IntrusiveDList::erase(const iterator &first, const iterator &limit) -> iterator +ts::IntrusiveDList::erase(const iterator &first, const iterator &limit) -> iterator { value_type *spot = first; value_type *prev{L::prev_ptr(spot)}; @@ -685,74 +741,142 @@ IntrusiveDList::erase(const iterator &first, const iterator &limit) -> iterat return {limit._v, this}; }; +template +auto +ts::IntrusiveDList::operator=(self_type &&that) -> self_type & +{ + if (this != &that) { + this->_head = that._head; + this->_tail = that._tail; + this->_count = that._count; + that.clear(); + } + return *this; +} + template size_t -IntrusiveDList::count() const +ts::IntrusiveDList::count() const { return _count; }; template auto -IntrusiveDList::begin() const -> const_iterator +ts::IntrusiveDList::begin() const -> const_iterator { return const_iterator{this, _head}; }; template auto -IntrusiveDList::begin() -> iterator +ts::IntrusiveDList::begin() -> iterator { return iterator{this, _head}; }; template auto -IntrusiveDList::end() const -> const_iterator +ts::IntrusiveDList::end() const -> const_iterator { return const_iterator{this, nullptr}; }; template auto -IntrusiveDList::end() -> iterator +ts::IntrusiveDList::end() -> iterator { return iterator{this, nullptr}; }; template auto -IntrusiveDList::iterator_for(value_type *v) -> iterator +ts::IntrusiveDList::iterator_for(value_type *v) -> iterator { return iterator{this, v}; }; template auto -IntrusiveDList::iterator_for(const value_type *v) const -> const_iterator +ts::IntrusiveDList::iterator_for(const value_type *v) const -> const_iterator { return const_iterator{this, v}; }; template auto -IntrusiveDList::tail() -> value_type * +ts::IntrusiveDList::tail() -> value_type * +{ + return _tail; +} + +template +auto +ts::IntrusiveDList::tail() const -> const value_type * { return _tail; } template auto -IntrusiveDList::head() -> value_type * +ts::IntrusiveDList::head() -> value_type * +{ + return _head; +} + +template +auto +ts::IntrusiveDList::head() const -> const value_type * { return _head; } template auto -IntrusiveDList::clear() -> self_type & +ts::IntrusiveDList::clear() -> self_type & { _head = _tail = nullptr; _count = 0; return *this; }; + +namespace detail +{ + // Make @c apply more convenient by allowing the function to take a reference type or pointer type + // to the container elements. The pointer type is the base, plus a shim to convert from a reference + // type functor to a pointer pointer type. The complex return type definition forces only one, but + // not both, to be valid for a particular functor. This also must be done via free functions and not + // method overloads because the compiler forces a match up of method definitions and declarations + // before any template instantiation. + + template + auto + Intrusive_DList_Apply(ts::IntrusiveDList &list, F &&f) + -> decltype(f(*static_cast::value_type *>(nullptr)), list) + { + return list.apply([&f](typename ts::IntrusiveDList::value_type *v) { return f(*v); }); + } + + template + auto + Intrusive_DList_Apply(ts::IntrusiveDList &list, F &&f) + -> decltype(f(static_cast::value_type *>(nullptr)), list) + { + auto spot{list.begin()}; + auto limit{list.end()}; + while (spot != limit) { + f(spot++); // post increment means @a spot is updated before @a f is applied. + } + return list; + } +} // namespace detail + +template +template +auto +ts::IntrusiveDList::apply(F &&f) -> self_type & +{ + return detail::Intrusive_DList_Apply(*this, f); +}; + +} // namespace ts diff --git a/include/tscpp/util/Makefile.am b/include/tscpp/util/Makefile.am index caeed36daab..5fea12cd2d1 100644 --- a/include/tscpp/util/Makefile.am +++ b/include/tscpp/util/Makefile.am @@ -19,5 +19,6 @@ library_includedir=$(includedir)/tscpp/util library_include_HEADERS = \ + IntrusiveDList.h \ PostScript.h \ TextView.h diff --git a/src/tscore/IntrusivePtrTest.cc b/src/tscore/IntrusivePtrTest.cc index 7edd6acbcb1..c7cb58c3f1c 100644 --- a/src/tscore/IntrusivePtrTest.cc +++ b/src/tscore/IntrusivePtrTest.cc @@ -22,7 +22,7 @@ */ #include "tscore/IntrusivePtr.h" -#include "tscore/IntrusiveDList.h" +#include "tscpp/util/IntrusiveDList.h" #include "tscore/TestBox.h" namespace diff --git a/src/tscore/IpMap.cc b/src/tscore/IpMap.cc index 7013029be55..ed206572c3c 100644 --- a/src/tscore/IpMap.cc +++ b/src/tscore/IpMap.cc @@ -282,8 +282,8 @@ namespace detail return n->_prev; } }; - using NodeList = IntrusiveDList; - // typedef IntrusiveDList NodeList; + using NodeList = ts::IntrusiveDList; + // typedef ts::IntrusiveDList NodeList; /// This keeps track of all allocated nodes in order. /// Iteration depends on this list being maintained. NodeList _list; diff --git a/src/tscore/Makefile.am b/src/tscore/Makefile.am index af29d3a6549..367cf8e900f 100644 --- a/src/tscore/Makefile.am +++ b/src/tscore/Makefile.am @@ -160,7 +160,6 @@ libtscore_la_SOURCES = \ ink_time.h \ ink_uuid.cc \ ink_uuid.h \ - IntrusiveDList.h \ IpMap.cc \ IpMapConf.cc \ IpMapConf.h \ @@ -257,7 +256,6 @@ test_tscore_SOURCES = \ unit_tests/test_Extendible.cc \ unit_tests/test_History.cc \ unit_tests/test_ink_inet.cc \ - unit_tests/test_IntrusiveDList.cc \ unit_tests/test_IntrusiveHashMap.cc \ unit_tests/test_IntrusivePtr.cc \ unit_tests/test_IpMap.cc \ diff --git a/src/tscpp/util/Makefile.am b/src/tscpp/util/Makefile.am index 67ca3be9763..8f0f4428aa8 100644 --- a/src/tscpp/util/Makefile.am +++ b/src/tscpp/util/Makefile.am @@ -27,6 +27,7 @@ AM_CPPFLAGS += -I$(abs_top_srcdir)/include libtscpputil_la_LDFLAGS = -no-undefined -version-info @TS_LIBTOOL_VERSION@ libtscpputil_la_SOURCES = \ + IntrusiveDList.h \ PostScript.h \ TextView.h TextView.cc diff --git a/src/tscore/unit_tests/test_IntrusiveDList.cc b/src/tscpp/util/unit_tests/test_IntrusiveDList.cc similarity index 85% rename from src/tscore/unit_tests/test_IntrusiveDList.cc rename to src/tscpp/util/unit_tests/test_IntrusiveDList.cc index 2a057ba30ad..cb77bf089cf 100644 --- a/src/tscore/unit_tests/test_IntrusiveDList.cc +++ b/src/tscpp/util/unit_tests/test_IntrusiveDList.cc @@ -4,20 +4,17 @@ @section license License - Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you under the Apache License, Version 2.0 (the - "License"); you may not use this file except in compliance - with the License. You may obtain a copy of the License at + Licensed to the Apache Software Foundation (ASF) under one or more contributor license + agreements. See the NOTICE file distributed with this work for additional information regarding + copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with the License. You may + obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and + Unless required by applicable law or agreed to in writing, software distributed under the + License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + express or implied. See the License for the specific language governing permissions and limitations under the License. */ @@ -26,10 +23,12 @@ #include #include -#include "tscore/IntrusiveDList.h" -#include "tscore/BufferWriter.h" +#include "tscpp/util/IntrusiveDList.h" +#include "tscpp/util/bwf_base.h" -#include +#include "catch.hpp" + +using ts::IntrusiveDList; // -------------------- // Code for documentation - placed here to guarantee the examples at least compile. @@ -82,7 +81,7 @@ Message::is_in_list() const class Container { using self_type = Container; - using MessageList = IntrusiveDList; + using MessageList = ts::IntrusiveDList; public: ~Container(); @@ -147,7 +146,7 @@ Container::print() const } } -TEST_CASE("IntrusiveDList Example", "[libts][IntrusiveDList]") +TEST_CASE("IntrusiveDList Example", "[libtscpputil][IntrusiveDList]") { Container container; @@ -210,10 +209,10 @@ class PrivateThing : protected Thing // If any lines above here are changed, the documentation must be updated. // -------------------- -using ThingList = IntrusiveDList; -using PrivateThingList = IntrusiveDList; +using ThingList = ts::IntrusiveDList; +using PrivateThingList = ts::IntrusiveDList; -TEST_CASE("IntrusiveDList", "[libts][IntrusiveDList]") +TEST_CASE("IntrusiveDList", "[libtscpputil][IntrusiveDList]") { ThingList list; int n;