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