Vectors#

pw_containers: Generic collections of objects for embedded devices

A vector is a one-dimensional array with a variable length.

pw::Vector#

pw::Vector class is similar to std::vector, except it is backed by a fixed-size buffer.

Vectors must be declared with an explicit maximum size (e.g. Vector<int, 10>) but vectors can be used and referred to without the max size template parameter (e.g. Vector<int>).

To allow referring to a pw::Vector without an explicit maximum size, all Vector classes inherit from the generic Vector<T>, which stores the maximum size in a variable. This allows Vectors to be used without having to know their maximum size at compile time. It also keeps code size small since function implementations are shared for all maximum sizes.

Example#

 1class Publisher {
 2 public:
 3  using Subscriber = pw::Function<void(const Message&)>;
 4  static constexpr size_t kMaxSubscribers = 10;
 5
 6  pw::Status Subscribe(Subscriber&& subscriber) {
 7    // Check if the vector's fixed capacity has been exhausted.
 8    if (subscribers_.full()) {
 9      return pw::Status::ResourceExhausted();
10    }
11
12    // Add the subscriber to the vector.
13    subscribers_.emplace_back(std::move(subscriber));
14    return pw::OkStatus();
15  }
16
17  void Publish(const Message& message) {
18    // Iterate over the vector.
19    for (auto& subscriber : subscribers_) {
20      subscriber(message);
21    }
22  }
23
24 private:
25  pw::Vector<Subscriber, kMaxSubscribers> subscribers_;
26};

Size report#

The tables below illustrate the following scenarios:

  • The memory and code size cost incurred by a adding a single Vector.

  • The memory and code size cost incurred by adding another Vector with the same type as the first scenario, but with a different size. As Vector is templated on both type and size, a different size results in additional code being generated.

  • The memory and code size cost incurred by adding another Vector with the same size as the first scenario, but with a different type. As Vector is templated on both type and size, a different size results in additional code being generated.

Label

Segment

Delta

Vector

FLASH

+124

[section .rodata]

+4

pw::containers::size_report::Measure()

+2

pw::allocator::LibCAllocator::DoReallocate()

-4

__bi_84

DEL

-12

__aeabi_memclr4

+4

vClearInterruptMaskFromISR

DEL

-8

__aeabi_memmove

NEW

+124

_ZN2pw10containers11size_report13MeasureVectorIjLj10ETpTnRiJENSt3__211__wrap_iterIPjEEEEiT2_S8_j

NEW

+120

pw::Vector<>::insert()

NEW

+88

pw::Vector<>::resize()

NEW

+54

pw::Vector<>::ShiftEntriesForInsert()

NEW

+44

pw::Vector<>::emplace_back<>()

NEW

+44

pw::containers::size_report::GetContainer<>()

NEW

+44

pw::containers::size_report::MeasureContainer<>()

NEW

+26

pw::Vector<>::Append()

NEW

+26

pw::Vector<>::CopyFrom<>()

NEW

+12

__aeabi_memclr

NEW

+12

pw::Vector<>::assign<>()

NEW

+8

__aeabi_memmove4

+712

RAM

-8

[section .data]

+1

__Thumbv6MABSLongThunk_best_effort_wfe_or_timeout

NEW

+48

pw::containers::size_report::GetContainer<>()::container

NEW

+39

pw::containers::size_report::GetItems<>()::items

+80

Additional Vector with the same item type but different size

FLASH

+2

pw::Vector<>::ShiftEntriesForInsert()

+44

pw::containers::size_report::GetContainer<>()

+44

pw::containers::size_report::MeasureContainer<>()

+16

pw::containers::size_report::Measure()

-2

pw::allocator::LibCAllocator::DoReallocate()

-4

vClearInterruptMaskFromISR

NEW

+124

_ZN2pw10containers11size_report13MeasureVectorIjLj9ETpTnRiJENSt3__211__wrap_iterIPjEEEEiT2_S8_j

+224

RAM

+44

pw::containers::size_report::GetContainer<>()::container

+44

Additional Vector with different item type but same size

FLASH

+2

pw::containers::size_report::SetBaseline()

+124

pw::Vector<>::insert()

+88

pw::Vector<>::resize()

+68

pw::Vector<>::ShiftEntriesForInsert()

+52

pw::Vector<>::emplace_back<>()

+44

pw::containers::size_report::GetContainer<>()

+44

pw::containers::size_report::MeasureContainer<>()

+20

pw::containers::size_report::Measure()

+26

pw::Vector<>::Append()

+26

pw::Vector<>::CopyFrom<>()

+4

vClearInterruptMaskFromISR

+12

pw::Vector<>::assign<>()

NEW

+130

_ZN2pw10containers11size_report13MeasureVectorIyLj10ETpTnRiJENSt3__211__wrap_iterIPyEEEEiT2_S8_j

+640

RAM

+92

pw::containers::size_report::GetContainer<>()::container

+84

pw::containers::size_report::GetItems<>()::items

-4

[section .data]

+172

pw::DynamicVector#

pw::DynamicVector is similar to pw::Vector, except that it uses pw::Allocator for memory operations.

pw::DynamicPtrVector#

pw::DynamicPtrVector behaves like a std::vector<T>, but stores pointers to objects of type T allocated using a pw::Allocator. This allows it to store objects that are not movable or copyable, or objects of polymorphic types, while still providing a vector-like interface.

To support this, pw::DynamicPtrVector differs from std::vector in a few ways:

  • Memory Management: It manages the lifetime of the objects it holds. When an element is removed (e.g., via pop_back, erase, or destruction of the vector), the object is destroyed and its memory deallocated.

  • Pointer Storage: The underlying storage is a DynamicVector<T*>. Functions like data() return T* const*.

  • Capacity: reserve_ptr and ptr_capacity refer to the capacity of the underlying pointer vector, not the objects themselves. Objects are allocated individually.

  • Emplace: emplace and emplace_back allow constructing derived classes (e.g. vec.emplace_back<Derived>(args...)).

API reference#

Moved: pw_containers_vectors