sandbox
Loading...
Searching...
No Matches
static_vector.hpp
1// SPDX-License-Identifier: MIT
2#ifndef LIBSBX_CONTAINERS_STATIC_VECTOR_HPP_
3#define LIBSBX_CONTAINERS_STATIC_VECTOR_HPP_
4
5#include <memory>
6#include <type_traits>
7#include <ranges>
8#include <array>
9
10#include <fmt/format.h>
11
12#include <libsbx/utility/assert.hpp>
13
14#include <libsbx/memory/aligned_storage.hpp>
15
16namespace sbx::containers {
17
24template<typename Type, std::size_t Capacity>
26
27public:
28
29 using value_type = Type;
30 using reference = value_type&;
31 using const_reference = const value_type&;
32 using pointer = value_type*;
33 using const_pointer = const value_type*;
34 using size_type = std::size_t;
35 using iterator = pointer;
36 using const_iterator = const_pointer;
37
38 constexpr static_vector() noexcept
39 : _size{0u} { }
40
41 constexpr static_vector(const static_vector& other) noexcept
42 : _size{other._size} {
43 for (auto i : std::views::iota(0u, _size)) {
44 std::construct_at(_ptr(i), other[i]);
45 }
46 }
47
48 constexpr static_vector(static_vector&& other) noexcept
49 : _size{other._size} {
50 for (auto i : std::views::iota(0u, _size)) {
51 std::construct_at(_ptr(i), std::move(other[i]));
52 }
53 }
54
55 constexpr static_vector(std::initializer_list<value_type> values) noexcept
56 : _size{values.size()} {
57 utility::assert_that(_size <= Capacity, "initializer list size exceeds capacity");
58
59 for (auto value : values) {
60 if constexpr (std::is_move_constructible_v<Type>) {
61 push_back(std::move(value));
62 } else {
63 push_back(value);
64 }
65 }
66 }
67
68 constexpr ~static_vector() noexcept {
69 clear();
70 }
71
72 constexpr auto operator=(static_vector other) noexcept -> static_vector& {
73 if (this != &other) {
74 other.swap(*this);
75 }
76
77 return *this;
78 }
79
80 constexpr auto size() const noexcept -> size_type {
81 return _size;
82 }
83
84 auto capacity() const noexcept -> size_type {
85 return Capacity;
86 }
87
88 constexpr auto is_empty() const noexcept -> bool {
89 return _size == 0u;
90 }
91
92 constexpr auto is_full() const noexcept -> bool {
93 return _size == Capacity;
94 }
95
96 constexpr auto begin() noexcept -> iterator {
97 return _ptr(0u);
98 }
99
100 constexpr auto begin() const noexcept -> const_iterator {
101 return _ptr(0u);
102 }
103
104 constexpr auto cbegin() const noexcept -> const_iterator {
105 return begin();
106 }
107
108 constexpr auto end() noexcept -> iterator {
109 return _ptr(_size);
110 }
111
112 constexpr auto end() const noexcept -> const_iterator {
113 return _ptr(_size);
114 }
115
116 constexpr auto cend() const noexcept -> const_iterator {
117 return end();
118 }
119
120 constexpr auto front() noexcept -> reference {
121 return *begin();
122 }
123
124 constexpr auto front() const noexcept -> const_reference {
125 return *begin();
126 }
127
128 constexpr auto back() noexcept -> reference {
129 return *std::prev(end());
130 }
131
132 constexpr auto back() const noexcept -> const_reference {
133 return *std::prev(end());
134 }
135
136 constexpr auto operator[](const size_type index) noexcept -> reference {
137 return *_ptr(index);
138 }
139
140 constexpr auto operator[](const size_type index) const noexcept -> const_reference {
141 return *_ptr(index);
142 }
143
144 constexpr auto at(const size_type index) -> reference {
145 utility::assert_that(index < _size, "index is out of range");
146
147 return *_ptr(index);
148 }
149
150 constexpr auto at(const size_type index) const -> const_reference {
151 utility::assert_that(index < _size, "index is out of range");
152
153 return *_ptr(index);
154 }
155
156 constexpr auto data() noexcept -> pointer {
157 return _ptr(0u);
158 }
159
160 constexpr auto data() const noexcept -> const_pointer {
161 return _ptr(0u);
162 }
163
164 constexpr auto push_back(const value_type& value) noexcept -> void {
165 if (is_full()) {
166 return;
167 }
168
169 std::construct_at(_ptr(_size), value);
170 ++_size;
171 }
172
173 constexpr auto push_back(value_type&& value) noexcept -> void {
174 if (is_full()) {
175 return;
176 }
177
178 std::construct_at(_ptr(_size), std::move(value));
179 ++_size;
180 }
181
182 template<typename... Args>
183 requires (std::is_constructible_v<Type, Args...>)
184 constexpr auto emplace_back(Args&&... args) noexcept -> void {
185 if (is_full()) {
186 return;
187 }
188
189 std::construct_at(_ptr(_size), std::forward<Args>(args)...);
190 ++_size;
191 }
192
193 constexpr auto pop_back() noexcept -> void {
194 if (is_empty()) {
195 return;
196 }
197
198 std::destroy_at(std::prev(end()));
199 --_size;
200 }
201
202 constexpr auto clear() noexcept -> void {
203 for (auto i : std::views::iota(0u, _size)) {
204 std::destroy_at(_ptr(i));
205 }
206
207 _size = 0u;
208 }
209
210 constexpr auto swap(static_vector& other) -> void {
211 using std::swap;
212
213 swap(_size, other._size);
214 swap(_buffer, other._buffer);
215 }
216
217private:
218
219 constexpr auto _ptr(const size_type index) noexcept -> pointer {
220 // utility::assert_that(index < Capacity, "index is out of range");
221 return std::launder(reinterpret_cast<pointer>(_buffer.data() + index));
222 }
223
224 constexpr auto _ptr(const size_type index) const noexcept -> const_pointer {
225 // utility::assert_that(index < Capacity, "index is out of range");
226 return std::launder(reinterpret_cast<const_pointer>(_buffer.data() + index));
227 }
228
229 size_type _size;
230 std::array<memory::storage_for_t<Type>, Capacity> _buffer;
231
232}; // class static_vector
233
234template<typename Type, std::size_t Capacity>
235auto operator==(const static_vector<Type, Capacity>& lhs, const static_vector<Type, Capacity>& rhs) -> bool {
236 return std::ranges::equal(lhs, rhs);
237}
238
239template<typename Type, std::size_t Capacity>
240auto swap(static_vector<Type, Capacity>& lhs, static_vector<Type, Capacity>& rhs) -> void {
241 lhs.swap(rhs);
242}
243
244} // namespace sbx::containers
245
246#endif // LIBSBX_CONTAINERS_STATIC_VECTOR_HPP_
static_vector implementation inspired by https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p08...
Definition: static_vector.hpp:25