blob: 07093db78f0be4639aa2d4974e00b4f5e152717c [file] [log] [blame]
Wyatt Heplerf9fb90f2020-09-30 18:59:33 -07001.. _module-pw_containers:
Wyatt Hepler3c4e5de2020-03-03 14:37:52 -08002
3-------------
4pw_containers
5-------------
Armando Montanez0054a9b2020-03-13 13:06:24 -07006The ``pw_containers`` module provides embedded-friendly container classes.
Wyatt Hepler3c4e5de2020-03-03 14:37:52 -08007
Armando Montanez516022c2020-05-14 09:12:19 -07008pw::Vector
9==========
10The Vector class is similar to ``std::vector``, except it is backed by a
Wyatt Hepler3c4e5de2020-03-03 14:37:52 -080011fixed-size buffer. Vectors must be declared with an explicit maximum size
12(e.g. ``Vector<int, 10>``) but vectors can be used and referred to without the
13max size template parameter (e.g. ``Vector<int>``).
14
15To allow referring to a ``pw::Vector`` without an explicit maximum size, all
16Vector classes inherit from the generic ``Vector<T>``, which stores the maximum
17size in a variable. This allows Vectors to be used without having to know
18their maximum size at compile time. It also keeps code size small since
19function implementations are shared for all maximum sizes.
20
Armando Montanez516022c2020-05-14 09:12:19 -070021pw::IntrusiveList
22=================
Wyatt Heplerb6884d72021-06-11 09:23:52 -070023IntrusiveList provides an embedded-friendly singly-linked intrusive list
24implementation. An intrusive list is a type of linked list that embeds the
25"next" pointer into the list object itself. This allows the construction of a
26linked list without the need to dynamically allocate list entries.
Armando Montanez516022c2020-05-14 09:12:19 -070027
Wyatt Heplerb6884d72021-06-11 09:23:52 -070028In C, an intrusive list can be made by manually including the "next" pointer as
29a member of the object's struct. ``pw::IntrusiveList`` uses C++ features to
30simplify the process of creating an intrusive list. ``pw::IntrusiveList``
31provides a class that list elements can inherit from. This protects the "next"
32pointer from being accessed by the item class, so only the ``pw::IntrusiveList``
33class can modify the list.
Kevin Zengb0bb8492021-01-11 21:32:24 -080034
Armando Montanez516022c2020-05-14 09:12:19 -070035Usage
36-----
Wyatt Heplerb6884d72021-06-11 09:23:52 -070037While the API of ``pw::IntrusiveList`` is similar to a ``std::forward_list``,
38there are extra steps to creating objects that can be stored in this data
39structure. Objects that will be added to a ``IntrusiveList<T>`` must inherit
40from ``IntrusiveList<T>::Item``. They can inherit directly from it or inherit
41from it through another base class. When an item is instantiated and added to a
42linked list, the pointer to the object is added to the "next" pointer of
43whichever object is the current tail.
Armando Montanez516022c2020-05-14 09:12:19 -070044
45That means two key things:
46
Wyatt Heplerb6884d72021-06-11 09:23:52 -070047 - An instantiated ``IntrusiveList<T>::Item`` must remain in scope for the
48 lifetime of the ``IntrusiveList`` it has been added to.
49 - A linked list item CANNOT be included in two lists. Attempting to do so
50 results in an assert failure.
Armando Montanez516022c2020-05-14 09:12:19 -070051
52.. code-block:: cpp
53
54 class Square
Prashanth Swaminathan65731872021-02-11 08:51:08 -080055 : public pw::IntrusiveList<Square>::Item {
Armando Montanez516022c2020-05-14 09:12:19 -070056 public:
57 Square(unsigned int side_length) : side_length(side_length) {}
58 unsigned long Area() { return side_length * side_length; }
59
60 private:
61 unsigned int side_length;
62 };
63
Prashanth Swaminathan65731872021-02-11 08:51:08 -080064 pw::IntrusiveList<Square> squares;
Armando Montanez516022c2020-05-14 09:12:19 -070065
66 Square small(1);
67 Square large(4000);
68 // These elements are not copied into the linked list, the original objects
69 // are just chained together and can be accessed via
70 // `IntrusiveList<Square> squares`.
71 squares.push_back(small);
72 squares.push_back(large);
73
74 {
Wyatt Heplerb6884d72021-06-11 09:23:52 -070075 // When different_scope goes out of scope, it removes itself from the list.
Armando Montanez516022c2020-05-14 09:12:19 -070076 Square different_scope = Square(5);
77 squares.push_back(&different_scope);
78 }
79
Wyatt Heplerb6884d72021-06-11 09:23:52 -070080 for (const auto& square : squares) {
81 PW_LOG_INFO("Found a square with an area of %lu", square.Area());
Armando Montanez516022c2020-05-14 09:12:19 -070082 }
83
Wyatt Hepler7db89d12022-02-02 16:02:55 -080084 // Like std::forward_list, an iterator is invalidated when the item it refers
85 // to is removed. It is *NOT* safe to remove items from a list while iterating
86 // over it in a range-based for loop.
87 for (const auto& square_bad_example : squares) {
88 if (square_bad_example.verticies() != 4) {
89 // BAD EXAMPLE of how to remove matching items from a singly linked list.
90 squares.remove(square_bad_example); // NEVER DO THIS! THIS IS A BUG!
91 }
92 }
93
94 // To remove items while iterating, use an iterator to the previous item.
95 auto previous = squares.before_begin();
96 auto current = squares.begin();
97
98 while (current != squares.end()) {
99 if (current->verticies() != 4) {
100 current = squares.erase_after(previous);
101 } else {
102 previous = current;
103 ++current;
104 }
Erik Gilling5733a0e2022-01-24 21:59:49 +0000105 }
106
Wyatt Heplerb6884d72021-06-11 09:23:52 -0700107pw::containers::FlatMap
108=======================
109FlatMap provides a simple, fixed-size associative array with lookup by key or
110value. ``pw::containers::FlatMap`` contains the same methods and features for
111looking up data as std::map. However, there are no methods that modify the
112underlying data. The underlying array in ``pw::containers::FlatMap`` does not
113need to be sorted. During construction, ``pw::containers::FlatMap`` will
114perform a constexpr insertion sort.
Armando Montanez516022c2020-05-14 09:12:19 -0700115
Wyatt Hepler100a1602021-10-05 14:02:02 -0700116pw::containers::FilteredView
117============================
118``pw::containers::FilteredView`` provides a view of a container that only
119contains elements that match the specified filter. This class is similar to
120C++20's `std::ranges::filter_view
121<https://en.cppreference.com/w/cpp/ranges/filter_view>`_.
122
123To create a ``FilteredView``, pass a container and a filter object, which may be
124a lambda or class that implements ``operator()`` for the container's value type.
125
126.. code-block:: cpp
127
128 std::array<int, 99> kNumbers = {3, 1, 4, 1, ...};
129
130 for (int even : FilteredView(kNumbers, [](int n) { return n % 2 == 0; })) {
131 PW_LOG_INFO("This number is even: %d", even);
132 }
133
Wyatt Heplerbfb98872021-10-07 21:16:24 -0700134pw::containers::WrappedIterator
135===============================
136``pw::containers::WrappedIterator`` is a class that makes it easy to wrap an
137existing iterator type. It reduces boilerplate by providing ``operator++``,
138``operator--``, ``operator==``, ``operator!=``, and the standard iterator
139aliases (``difference_type``, ``value_type``, etc.). It does not provide the
140dereference operator; that must be supplied by a derived class.
141
142To use it, create a class that derives from ``WrappedIterator`` and define
143``operator*()`` and ``operator->()`` as appropriate. The new iterator might
144apply a transformation to or access a member of the values provided by the
145original iterator. The following example defines an iterator that multiplies the
146values in an array by 2.
147
148.. code-block:: cpp
149
150 // Divides values in a std::array by two.
151 class DoubleIterator
152 : public pw::containers::WrappedIterator<DoubleIterator, const int*, int> {
153 public:
154 constexpr DoubleIterator(const int* it) : WrappedIterator(it) {}
155
156 int operator*() const { return value() * 2; }
157
158 // Don't define operator-> since this iterator returns by value.
159 };
160
161 constexpr std::array<int, 6> kArray{0, 1, 2, 3, 4, 5};
162
163 void SomeFunction {
164 for (DoubleIterator it(kArray.begin()); it != DoubleIterator(kArray.end()); ++it) {
165 // The iterator yields 0, 2, 4, 6, 8, 10 instead of the original values.
166 }
167 };
168
169``WrappedIterator`` may be used in concert with ``FilteredView`` to create a
170view that iterates over a matching values in a container and applies a
171transformation to the values. For example, it could be used with
172``FilteredView`` to filter a list of packets and yield only one field from the
173packet.
174
175The combination of ``FilteredView`` and ``WrappedIterator`` provides some basic
176functional programming features similar to (though much more cumbersome than)
177`generator expressions <https://www.python.org/dev/peps/pep-0289/>`_ (or `filter
178<https://docs.python.org/3/library/functions.html#filter>`_/`map
179<https://docs.python.org/3/library/functions.html#map>`_) in Python or streams
180in Java 8. ``WrappedIterator`` and ``FilteredView`` require no memory
181allocation, which is helpful when memory is too constrained to process the items
182into a new container.
183
Wyatt Heplerfff3c7c2021-08-17 18:15:10 -0700184pw::containers::to_array
185========================
186``pw::containers::to_array`` is a C++14-compatible implementation of C++20's
187`std::to_array <https://en.cppreference.com/w/cpp/container/array/to_array>`_.
188It converts a C array to a ``std::array``.
189
Wyatt Hepler3c4e5de2020-03-03 14:37:52 -0800190Compatibility
191=============
Wyatt Hepler3c4e5de2020-03-03 14:37:52 -0800192* C++17
193
194Dependencies
195============
Armando Montanez0054a9b2020-03-13 13:06:24 -0700196* ``pw_span``
Yuval Peressb8f3ad22021-10-26 22:55:27 -0600197
198Zephyr
199======
200To enable ``pw_containers`` for Zephyr add ``CONFIG_PIGWEED_CONTAINERS=y`` to
201the project's configuration.