1 // Copyright 2020 The Pigweed Authors
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
7 // https://www.apache.org/licenses/LICENSE-2.0
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
17 #include <initializer_list>
18 #include <type_traits>
20 #include "pw_containers/internal/intrusive_list_impl.h"
24 // IntrusiveList provides singly-linked list functionality for derived
25 // class items. IntrusiveList<T> is a handle to access and manipulate the
26 // list, and IntrusiveList<T>::Item is a base class items must inherit
27 // from. An instantiation of the derived class becomes a list item when inserted
28 // into a IntrusiveList.
30 // This has two important ramifications:
32 // - An instantiated IntrusiveList::Item must remain in scope for the
33 // lifetime of the IntrusiveList it has been added to.
34 // - A linked list item CANNOT be included in two lists, as it is part of a
35 // preexisting list and adding it to another implicitly breaks correctness of
41 // : public containers::IntrusiveList<TestItem>::Item {}
43 // containers::IntrusiveList<TestItem> test_items;
45 // auto item = TestItem();
46 // test_items.push_back(item);
48 // for (auto& test_item : test_items) {
55 class Item : public intrusive_list_impl::List::Item {
57 constexpr Item() = default;
60 using element_type = T;
61 using value_type = std::remove_cv_t<T>;
64 using iterator = intrusive_list_impl::Iterator<T, Item>;
65 using const_iterator =
66 intrusive_list_impl::Iterator<std::add_const_t<T>, const Item>;
68 constexpr IntrusiveList() { CheckItemType(); }
70 // Constructs an IntrusiveList from an iterator over Items. The iterator may
71 // dereference as either Item& (e.g. from std::array<Item>) or Item* (e.g.
72 // from std::initializer_list<Item*>).
73 template <typename Iterator>
74 IntrusiveList(Iterator first, Iterator last) : list_(first, last) {
78 // Constructs an IntrusiveList from a std::initializer_list of pointers to
80 IntrusiveList(std::initializer_list<Item*> items)
81 : IntrusiveList(items.begin(), items.end()) {}
83 template <typename Iterator>
84 void assign(Iterator first, Iterator last) {
85 list_.assign(first, last);
88 void assign(std::initializer_list<Item*> items) {
89 list_.assign(items.begin(), items.end());
92 [[nodiscard]] bool empty() const noexcept { return list_.empty(); }
94 void push_front(T& item) { list_.insert_after(list_.before_begin(), item); }
96 void push_back(T& item) { list_.insert_after(list_.before_end(), item); }
98 iterator insert_after(iterator pos, T& item) {
99 list_.insert_after(&(*pos), item);
100 return iterator(&item);
103 // Removes the first item in the list. The list must not be empty.
104 void pop_front() { list_.erase_after(list_.before_begin()); }
106 // Removes the item following pos from the list. The item is not destructed.
107 iterator erase_after(iterator pos) {
108 list_.erase_after(&(*pos));
112 // Removes all items from the list. The items themselves are not destructed.
113 void clear() { list_.clear(); }
115 // Removes this specific item from the list, if it is present. Finds the item
116 // by identity (address comparison) rather than value equality. Returns true
117 // if the item was removed; false if it was not present.
118 bool remove(const T& item) { return list_.remove(item); }
120 // Reference to the first element in the list. Undefined behavior if empty().
121 T& front() { return *static_cast<T*>(list_.begin()); }
123 // Reference to the last element in the list. Undefined behavior if empty().
124 T& back() { return *static_cast<T*>(list_.before_end()); }
126 // As in std::forward_list, returns the iterator before the begin() iterator.
127 iterator before_begin() noexcept {
128 return iterator(static_cast<Item*>(list_.before_begin()));
130 const_iterator before_begin() const noexcept {
131 return const_iterator(static_cast<const Item*>(list_.before_begin()));
133 const_iterator cbefore_begin() const noexcept { return before_begin(); }
135 iterator begin() noexcept {
136 return iterator(static_cast<Item*>(list_.begin()));
138 const_iterator begin() const noexcept {
139 return const_iterator(static_cast<const Item*>(list_.begin()));
141 const_iterator cbegin() const noexcept { return begin(); }
143 iterator end() noexcept { return iterator(static_cast<Item*>(list_.end())); }
144 const_iterator end() const noexcept {
145 return const_iterator(static_cast<const Item*>(list_.end()));
147 const_iterator cend() const noexcept { return end(); }
149 // Operation is O(size).
150 size_t size() const { return list_.size(); }
153 // Check that T is an Item in a function, since the class T will not be fully
154 // defined when the IntrusiveList<T> class is instantiated.
155 static constexpr void CheckItemType() {
157 std::is_base_of<Item, T>(),
158 "IntrusiveList items must be derived from IntrusiveList<T>::Item");
161 intrusive_list_impl::List list_;