iceoryx2
C++ Language Bindings
Loading...
Searching...
No Matches
static_vector.hpp
Go to the documentation of this file.
1// Copyright (c) 2025 Contributors to the Eclipse Foundation
2//
3// See the NOTICE file(s) distributed with this work for additional
4// information regarding copyright ownership.
5//
6// This program and the accompanying materials are made available under the
7// terms of the Apache Software License 2.0 which is available at
8// https://www.apache.org/licenses/LICENSE-2.0, or the MIT license
9// which is available at https://opensource.org/licenses/MIT.
10//
11// SPDX-License-Identifier: Apache-2.0 OR MIT
12
13#ifndef IOX2_INCLUDE_GUARD_BB_STATIC_VECTOR_HPP
14#define IOX2_INCLUDE_GUARD_BB_STATIC_VECTOR_HPP
15
17#include "iox2/bb/optional.hpp"
18
20
21#include <algorithm>
22#include <cstddef>
23#include <cstdint>
24#include <cstring>
25#include <functional>
26#include <initializer_list>
27#include <ostream>
28#include <type_traits>
29
30namespace iox2 {
31namespace bb {
32
34template <typename T, uint64_t Capacity>
36 static_assert(Capacity > 0, "Static container with capacity 0 is not allowed.");
37 static_assert(std::is_standard_layout<T>::value, "Containers can only be used with standard layout types.");
38
39 public:
40 using ValueType = T;
41 using SizeType = size_t;
42 using DifferenceType = ptrdiff_t;
43 using Reference = T&;
44 using ConstReference = T const&;
45 using Pointer = T*;
46 using ConstPointer = T const*;
51
55 friend class StaticVector;
56
57 private:
58 StaticVector const* m_parent;
59
60 constexpr explicit UncheckedConstAccessor(StaticVector const& parent)
61 : m_parent(&parent) {
62 }
63
64 public:
67 // NOTE: can be changed to '= delete' when C++17 becomes mandatory and we can rely on RVO
71
72 constexpr auto operator[](SizeType index) const&& -> ConstReference {
73 return *m_parent->m_storage.pointer_from_index(index);
74 }
75
76 constexpr auto begin() const&& noexcept -> ConstIterator {
77 return m_parent->m_storage.pointer_from_index(0);
78 }
79
80 constexpr auto end() const&& noexcept -> ConstIterator {
81 return m_parent->m_storage.pointer_from_index(m_parent->m_storage.size());
82 }
83
84 constexpr auto data() const&& noexcept -> ConstPointer {
85 return m_parent->m_storage.pointer_from_index(0);
86 }
87 };
88
89 // Mutable unchecked element access.
92 friend class StaticVector;
93
94 private:
95 StaticVector* m_parent;
96
97 constexpr explicit UncheckedAccessor(StaticVector& parent)
98 : m_parent(&parent) {
99 }
100
101 public:
104 // NOTE: can be changed to '= delete' when C++17 becomes mandatory and we can rely on RVO
108
109 constexpr auto operator[](SizeType index) && -> Reference {
110 return *m_parent->m_storage.pointer_from_index(index);
111 }
112
113 constexpr auto begin() && noexcept -> Iterator {
114 return m_parent->m_storage.pointer_from_index(0);
115 }
116
117 constexpr auto end() && noexcept -> Iterator {
118 return m_parent->m_storage.pointer_from_index(this->m_parent->m_storage.size());
119 }
120
121 constexpr auto data() && noexcept -> Pointer {
122 return m_parent->m_storage.pointer_from_index(0);
123 }
124 };
125
126 private:
127 template <typename, uint64_t>
128 friend class StaticVector;
130 StorageType m_storage;
131
132 public:
133 // constructors
134 constexpr StaticVector() noexcept = default;
135 constexpr StaticVector(StaticVector const&) = default;
136 constexpr StaticVector(StaticVector&&) = default;
137
139 template <uint64_t M, std::enable_if_t<(Capacity >= M), bool> = true>
140 // NOLINTNEXTLINE(hicpp-explicit-conversions), conceptually a copy constructor
141 constexpr StaticVector(StaticVector<T, M> const& rhs)
142 : m_storage(rhs.m_storage) {
143 }
144
146 template <uint64_t M, std::enable_if_t<(Capacity >= M), bool> = true>
147 // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays), static bounds
148 constexpr explicit StaticVector(T const (&element_array)[M]) {
149 for (auto& element : element_array) {
150 try_push_back(element);
151 }
152 }
153
154 // destructor
156
157 // assignment
158 auto operator=(StaticVector const&) -> StaticVector& = default;
159 auto operator=(StaticVector&&) -> StaticVector& = default;
160
161 template <uint64_t N>
162 static constexpr auto from_value(const T& value) -> StaticVector {
163 static_assert(N <= Capacity, "Trying to initialize a StaticVector beyond its capacity!");
164
165 auto vec = StaticVector {};
166 vec.m_storage.insert_at(0, N, value);
167 return vec;
168 }
169
173 template <typename U = T, std::enable_if_t<std::is_default_constructible<U>::value, bool> = true>
174 static constexpr auto from_value(SizeType count) -> bb::Optional<StaticVector> {
175 if (count <= Capacity) {
176 return from_value(count, T {});
177 } else {
178 return bb::NULLOPT;
179 }
180 }
181
183 static constexpr auto from_value(SizeType count, T const& value) -> bb::Optional<StaticVector> {
184 // we define ret here to encourage return-value-optimization
186 if (count <= Capacity) {
187 ret = StaticVector {};
188 ret->m_storage.insert_at(0, count, value);
189 } else {
190 ret = bb::NULLOPT;
191 }
192 return ret;
193 }
194
199 template <typename Iter,
200 typename Sentinel,
201 std::enable_if_t<
202 std::is_constructible<T, decltype(*std::declval<Iter>())>::value
203 && std::is_convertible<decltype(std::declval<Iter>() == std::declval<Sentinel>()), bool>::value,
204 bool> = true>
205 static constexpr auto from_range_unchecked(Iter it_begin, Sentinel it_end) -> bb::Optional<StaticVector> {
206 // we define ret here to encourage return-value-optimization
208 for (auto it = it_begin; it != it_end; ++it) {
209 if (!ret->try_push_back(*it)) {
210 ret = bb::NULLOPT;
211 break;
212 }
213 }
214 return ret;
215 }
216
221 template <typename Range>
222 static constexpr auto from_range_unchecked(Range const& rng) -> bb::Optional<StaticVector> {
223 using std::begin;
224 using std::end;
225 return from_range_unchecked(begin(rng), end(rng));
226 }
227
228
232 static constexpr auto from_initializer_list(std::initializer_list<T> init_list) -> bb::Optional<StaticVector> {
233 if (init_list.size() > Capacity) {
234 return bb::NULLOPT;
235 } else {
236 return from_range_unchecked(begin(init_list), end(init_list));
237 }
238 }
239
243 template <typename... Args, std::enable_if_t<std::is_constructible<T, Args...>::value, bool> = true>
244 constexpr auto try_emplace_back(Args&&... args) -> bool {
245 if (m_storage.size() < Capacity) {
246 m_storage.emplace_back(std::forward<Args>(args)...);
247 return true;
248 } else {
249 return false;
250 }
251 }
252
257 template <typename... Args, std::enable_if_t<std::is_constructible<T, Args...>::value, bool> = true>
258 constexpr auto try_emplace_at(SizeType index, Args&&... args) -> bool {
259 if ((m_storage.size() < Capacity) && (index <= m_storage.size())) {
260 m_storage.emplace_at(index, std::forward<Args>(args)...);
261 return true;
262 } else {
263 return false;
264 }
265 }
266
270 constexpr auto try_erase_at(SizeType index) -> bool {
271 if (index < m_storage.size()) {
272 m_storage.erase_at(index);
273 return true;
274 } else {
275 return false;
276 }
277 }
278
282 constexpr auto try_erase_at(SizeType begin_index, SizeType end_index) -> bool {
283 if ((end_index <= m_storage.size()) && (begin_index <= end_index)) {
284 m_storage.erase_at(begin_index, end_index);
285 return true;
286 } else {
287 return false;
288 }
289 }
290
295 constexpr auto try_insert_at(SizeType index, T const& value) -> bool {
296 return try_emplace_at(index, value);
297 }
298
303 constexpr auto try_insert_at(SizeType index, T&& value) -> bool {
304 return try_emplace_at(index, std::move(value));
305 }
306
311 constexpr auto try_insert_at(SizeType index, SizeType count, T const& value) -> bool {
312 if ((index <= m_storage.size()) && (m_storage.size() + count <= Capacity)) {
313 m_storage.insert_at(index, count, value);
314 return true;
315 } else {
316 return false;
317 }
318 }
319
325 template <typename Iter,
326 typename Sentinel,
327 std::enable_if_t<
328 std::is_constructible<T, decltype(*std::declval<Iter>())>::value
329 && std::is_convertible<decltype(std::declval<Iter>() == std::declval<Sentinel>()), bool>::value,
330 bool> = true>
331 constexpr auto try_insert_at_unchecked(SizeType index, Iter it_begin, Sentinel it_end) -> bool {
332 if (index <= m_storage.size()) {
333 auto const old_size = size();
334 for (auto it = it_begin; it != it_end; ++it) {
335 if (!try_push_back(*it)) {
336 m_storage.shrink_from_back(old_size);
337 return false;
338 }
339 }
340 m_storage.rotate_from_back(index, old_size);
341 return true;
342 } else {
343 return false;
344 }
345 }
346
351 constexpr auto try_insert_at_unchecked(SizeType index, std::initializer_list<T> init_list) {
352 return try_insert_at_unchecked(index, init_list.begin(), init_list.end());
353 }
354
357 constexpr void clear() {
358 m_storage.erase_at(0, m_storage.size());
359 }
360
365 constexpr auto try_push_back(T const& value) -> bool {
366 return try_emplace_back(value);
367 }
368
373 constexpr auto try_push_back(T&& value) -> bool {
374 return try_emplace_back(std::move(value));
375 }
376
380 constexpr auto try_pop_back() -> bool {
381 if (m_storage.size() > 0) {
382 m_storage.shrink_from_back(m_storage.size() - 1);
383 return true;
384 } else {
385 return false;
386 }
387 }
388
390 static constexpr auto capacity() noexcept -> SizeType {
391 return Capacity;
392 }
393
395 constexpr auto size() const noexcept -> SizeType {
396 return m_storage.size();
397 }
398
400 constexpr auto empty() const -> bool {
401 return size() == 0;
402 }
403
408 if (index < m_storage.size()) {
409 return *m_storage.pointer_from_index(index);
410 } else {
411 return bb::NULLOPT;
412 }
413 }
414
419 if (index < m_storage.size()) {
420 return *m_storage.pointer_from_index(index);
421 } else {
422 return bb::NULLOPT;
423 }
424 }
425
430 if (!empty()) {
431 return *m_storage.pointer_from_index(0);
432 } else {
433 return bb::NULLOPT;
434 }
435 }
436
441 if (!empty()) {
442 return *m_storage.pointer_from_index(0);
443 } else {
444 return bb::NULLOPT;
445 }
446 }
447
452 if (!empty()) {
453 return *m_storage.pointer_from_index(size() - 1);
454 } else {
455 return bb::NULLOPT;
456 }
457 }
458
463 if (!empty()) {
464 return *m_storage.pointer_from_index(size() - 1);
465 } else {
466 return bb::NULLOPT;
467 }
468 }
469
472 return UncheckedAccessor { *this };
473 }
474
477 return UncheckedConstAccessor { *this };
478 }
479
480 // comparison operators
481 friend auto operator==(StaticVector const& lhs, StaticVector const& rhs) -> bool {
482 return std::equal(lhs.unchecked_access().begin(),
483 lhs.unchecked_access().end(),
484 rhs.unchecked_access().begin(),
485 rhs.unchecked_access().end());
486 }
487
488 friend auto operator!=(StaticVector const& lhs, StaticVector const& rhs) -> bool {
489 return !(lhs == rhs);
490 }
491
494 constexpr auto static_memory_layout_metrics() noexcept {
495 struct VectorMemoryLayoutMetrics {
496 size_t vector_alignment;
497 size_t vector_size;
498 typename StorageType::StorageMemoryLayoutMetrics storage_metrics;
499 } ret;
500 using Self = std::remove_reference_t<decltype(*this)>;
501 ret.vector_size = sizeof(Self);
502 ret.vector_alignment = alignof(Self);
503 ret.storage_metrics = m_storage.static_memory_layout_metrics();
504 return ret;
505 }
506};
507
508template <typename>
509struct IsStaticVector : std::false_type { };
510
511template <typename T, uint64_t N>
512struct IsStaticVector<StaticVector<T, N>> : std::true_type { };
513
514} // namespace bb
515} // namespace iox2
516
517template <typename T, uint64_t N>
518auto operator<<(std::ostream& stream, const iox2::bb::StaticVector<T, N>& value) -> std::ostream& {
519 stream << "StaticVector::<" << N << "> { m_size: " << value.size() << ", m_data: [ ";
520 if (!value.empty()) {
521 stream << value.unchecked_access()[0];
522 }
523 for (uint64_t idx = 1; idx < value.size(); ++idx) {
524 stream << ", " << value.unchecked_access()[idx];
525 }
526 stream << " ] }";
527 return stream;
528}
529
530#endif // IOX2_INCLUDE_GUARD_BB_STATIC_VECTOR_HPP
#define IOX2_CONSTEXPR_DTOR
Users of this class must ensure that all memory accesses stay within bounds of the accessed vector's ...
UncheckedAccessor(UncheckedAccessor const &)=delete
auto operator=(UncheckedAccessor &&) -> UncheckedAccessor &=delete
UncheckedAccessor(UncheckedAccessor &&)=default
constexpr auto operator[](SizeType index) &&-> Reference
auto operator=(UncheckedAccessor const &) -> UncheckedAccessor &=delete
constexpr auto data() &&noexcept -> Pointer
constexpr auto end() &&noexcept -> Iterator
constexpr auto begin() &&noexcept -> Iterator
constexpr auto data() const &&noexcept -> ConstPointer
UncheckedConstAccessor(UncheckedConstAccessor &&)=default
constexpr auto operator[](SizeType index) const &&-> ConstReference
auto operator=(UncheckedConstAccessor &&) -> UncheckedConstAccessor &=delete
constexpr auto begin() const &&noexcept -> ConstIterator
constexpr auto end() const &&noexcept -> ConstIterator
UncheckedConstAccessor(UncheckedConstAccessor const &)=delete
auto operator=(UncheckedConstAccessor const &) -> UncheckedConstAccessor &=delete
A resizable container with compile-time fixed static capacity and contiguous inplace storage.
constexpr StaticVector(T const (&element_array)[M])
Construct from a C-style array.
auto back_element() -> OptionalReference
auto operator=(StaticVector &&) -> StaticVector &=default
auto unchecked_access() -> UncheckedAccessor
Unchecked mutable access to the vector contents.
constexpr auto try_erase_at(SizeType begin_index, SizeType end_index) -> bool
bb::Optional< std::reference_wrapper< T const > > OptionalConstReference
constexpr auto empty() const -> bool
Checks whether the vector is currently empty.
constexpr auto try_insert_at(SizeType index, T const &value) -> bool
constexpr auto try_emplace_at(SizeType index, Args &&... args) -> bool
auto unchecked_access() const -> UncheckedConstAccessor
Unchecked immutable access to the vector contents.
constexpr auto try_insert_at_unchecked(SizeType index, Iter it_begin, Sentinel it_end) -> bool
constexpr StaticVector() noexcept=default
friend auto operator!=(StaticVector const &lhs, StaticVector const &rhs) -> bool
constexpr auto try_insert_at_unchecked(SizeType index, std::initializer_list< T > init_list)
static constexpr auto from_range_unchecked(Range const &rng) -> bb::Optional< StaticVector >
static constexpr auto capacity() noexcept -> SizeType
Retrieves the static capacity of the vector.
constexpr auto try_erase_at(SizeType index) -> bool
constexpr auto try_insert_at(SizeType index, SizeType count, T const &value) -> bool
static constexpr auto from_initializer_list(std::initializer_list< T > init_list) -> bb::Optional< StaticVector >
static constexpr auto from_value(const T &value) -> StaticVector
constexpr auto try_pop_back() -> bool
auto operator=(StaticVector const &) -> StaticVector &=default
auto element_at(SizeType index) -> OptionalReference
constexpr auto static_memory_layout_metrics() noexcept
static constexpr auto from_value(SizeType count) -> bb::Optional< StaticVector >
auto back_element() const -> OptionalConstReference
static constexpr auto from_value(SizeType count, T const &value) -> bb::Optional< StaticVector >
Construct a new vector with count copies of value.
auto element_at(SizeType index) const -> OptionalConstReference
static constexpr auto from_range_unchecked(Iter it_begin, Sentinel it_end) -> bb::Optional< StaticVector >
constexpr auto try_emplace_back(Args &&... args) -> bool
friend auto operator==(StaticVector const &lhs, StaticVector const &rhs) -> bool
constexpr auto try_push_back(T const &value) -> bool
constexpr auto try_insert_at(SizeType index, T &&value) -> bool
constexpr auto try_push_back(T &&value) -> bool
constexpr auto size() const noexcept -> SizeType
Retrieves the size of the vector.
IOX2_CONSTEXPR_DTOR ~StaticVector()=default
bb::Optional< std::reference_wrapper< T > > OptionalReference
auto front_element() -> OptionalReference
auto front_element() const -> OptionalConstReference
constexpr void insert_at(uint64_t index, uint64_t count, T const &value)
auto constexpr size() const noexcept -> uint64_t
constexpr void shrink_from_back(uint64_t target_size)
auto pointer_from_index(uint64_t idx) -> T *
constexpr void emplace_back(Args &&... args)
constexpr void erase_at(uint64_t index)
constexpr void rotate_from_back(uint64_t index_to, uint64_t index_first_from)
constexpr auto static_memory_layout_metrics() noexcept -> StorageMemoryLayoutMetrics
constexpr void emplace_at(uint64_t index, Args &&... args)
constexpr NulloptT NULLOPT
Definition optional.hpp:28
iox2::bb::variation::Optional< T > Optional
Definition optional.hpp:25
auto operator<<(std::ostream &stream, const iox2::bb::StaticVector< T, N > &value) -> std::ostream &