Cuda battery library
Loading...
Searching...
No Matches
vector.hpp
Go to the documentation of this file.
1// Copyright 2021 Pierre Talbot
2
3#ifndef CUDA_BATTERY_VECTOR_HPP
4#define CUDA_BATTERY_VECTOR_HPP
5
6#include "utility.hpp"
7#include "allocator.hpp"
8#include <memory>
9#include <initializer_list>
10#include <vector>
11
12/** \file vector.hpp
13 * A partial implementation of `std::vector`, with additional constructors to convert from `std::vector`.
14 * The allocator is scoped, meaning it will be passed to the constructor of `T` if `T` provides a suited constructor with an allocator.
15*/
16
17namespace battery {
18
19template<class T, class Allocator = standard_allocator>
20class vector {
21
22public:
23 using value_type = T;
24 using allocator_type = Allocator;
26
27private:
28 static constexpr const size_t GROWING_FACTOR = 3;
29 size_t n;
30 size_t cap;
31 allocator_type allocator;
32 value_type* data_;
33
34 CUDA T* allocate() {
35 if(cap > 0) {
36 return static_cast<T*>(allocator.allocate(sizeof(T) * cap));
37 }
38 else {
39 return nullptr;
40 }
41 }
42
43 CUDA void deallocate() {
44 allocator.deallocate(data_);
45 }
46
47 CUDA NI void reallocate(size_t new_cap) {
48 size_t n2 = n;
49 value_type* data2 = data_;
50 cap = new_cap;
51 n = min(new_cap, n); // in case we shrink the initial array.
52 data_ = allocate();
53 for(size_t i = 0; i < n; ++i) {
54 if constexpr(std::is_move_constructible_v<value_type>) {
55 new(&data_[i]) value_type(std::move(data2[i]));
56 }
57 else {
58 new(&data_[i]) value_type(data2[i]);
59 }
60 }
61 // Free the old data array that has been reallocated.
62 for(size_t i = 0; i < n2; ++i) {
63 data2[i].~T();
64 }
65 allocator.deallocate(data2);
66 }
67
68 // Reallocate memory if the current array is full.
69 CUDA void reallocate() {
70 if(n == cap) {
71 reallocate(max(cap, size_t(1)) * GROWING_FACTOR);
72 }
73 }
74
75 template<class T2, class Allocator2> friend class vector;
76
77 CUDA void inplace_new(size_t i) {
78 if constexpr(std::is_constructible<value_type, const allocator_type&>{}) {
79 new(&data_[i]) value_type(allocator);
80 }
81 else {
82 new(&data_[i]) value_type{};
83 }
84 }
85
86 template <class U>
87 CUDA void inplace_new(size_t i, const U& value) {
88 if constexpr(std::is_constructible<value_type, const U&, const allocator_type&>{}) {
89 new(&data_[i]) value_type(value, allocator);
90 }
91 else {
92 new(&data_[i]) value_type(value);
93 }
94 }
95
96public:
97 /** Allocate an array of size `n` using `allocator`, with `n` default-inserted instances of `T`.
98 * `Allocator` is scoped, meaning it will be passed to the constructor of `T` if `T(const Allocator&)` exists, otherwise `T()` is called. */
99 CUDA NI vector(size_t n, const allocator_type& alloc = allocator_type()):
100 n(n), cap(n), allocator(alloc), data_(allocate())
101 {
102 for(size_t i = 0; i < n; ++i) {
103 inplace_new(i);
104 }
105 }
106
107 /** Default constructor.*/
109 : n(0), cap(0), allocator(alloc), data_(nullptr) {}
110
111 /** Allocate an array of size `n` using `allocator`.
112 Initialize the elements of the array with those of the array `from`.
113 `Allocator` is scoped, meaning it will be passed to the constructor of `T` if `T(const T&, const Allocator&)` exists, otherwise `T(const T&)` is called. */
114 template <class U>
115 CUDA NI vector(const U* from, size_t n, const allocator_type& alloc = allocator_type{})
116 : n(n), cap(n), allocator(alloc), data_(allocate())
117 {
118 for(size_t i = 0; i < n; ++i) {
119 inplace_new(i, from[i]);
120 }
121 }
122
123 /** Copy constructor with an allocator, useful when the vector we copied from was declared using another allocator. */
124 template <class U, class Allocator2>
126 : this_type(from.data(), from.size(), alloc) {}
127
128 /** Copy constructor with different underlying types. */
129 template <class U>
131 : this_type(from, from.allocator) {}
132
133 /** Redefine the copy constructor to be sure it calls a constructor with an allocator. */
134 CUDA vector(const this_type& from)
135 : this_type(from, from.allocator) {}
136
137 /** Initialize of an array of size `n` with each element initialized to `from` using `allocator`.
138 * `Allocator` is scoped, meaning it will be passed to the constructor of `T` if `T(const T&, const Allocator&)` exists, otherwise `T(const T&)` is called. */
139 template <class U>
140 CUDA NI vector(size_t n, const U& from, const allocator_type& alloc = allocator_type{})
141 : n(n), cap(n), allocator(alloc), data_(allocate())
142 {
143 for(size_t i = 0; i < n; ++i) {
144 inplace_new(i, from);
145 }
146 }
147
148 CUDA NI vector(this_type&& other): this_type(other.allocator) {
149 swap(other);
150 }
151
152 CUDA NI vector(std::initializer_list<T> init, const allocator_type& alloc = allocator_type{})
153 : n(init.size()), cap(init.size()), allocator(alloc), data_(allocate())
154 {
155 size_t i = 0;
156 for(const T& v : init) {
157 inplace_new(i, v);
158 ++i;
159 }
160 }
161
162 template <class U, class Alloc2>
163 vector(const std::vector<U, Alloc2>& other, const allocator_type& alloc = allocator_type{})
164 : n(other.size()), cap(other.size()), allocator(alloc), data_(allocate())
165 {
166 for(size_t i = 0; i < n; ++i) {
167 inplace_new(i, other[i]);
168 }
169 }
170
172 for(size_t i = 0; i < n; ++i) {
173 data_[i].~T();
174 }
175 allocator.deallocate(data_);
176 data_ = nullptr;
177 n = 0;
178 cap = 0;
179 }
180
182 swap(other);
183 return *this;
184 }
185
186private:
187 template <class U, class Allocator2>
188 CUDA this_type& assignment(const vector<U, Allocator2>& other) {
189 reserve(other.size());
190 for(size_t i = 0; i < other.n; ++i) {
191 if(i < n) {
192 data_[i].~value_type();
193 }
194 inplace_new(i, other.data_[i]);
195 }
196 for(size_t i = other.n; i < n; ++i) {
197 data_[i].~value_type();
198 }
199 n = other.n;
200 return *this;
201 }
202
203public:
205 return assignment(other);
206 }
207
208 /** Beware that this operator does not free the memory of `this`, the capacity remains unchanged. */
209 template <class U, class Allocator2>
211 return assignment(other);
212 }
213
215 return allocator;
216 }
217
219 assert(i < n);
220 return data_[i];
221 }
222
223 CUDA const value_type& operator[](size_t i) const {
224 assert(i < n);
225 return data_[i];
226 }
227
228 CUDA value_type& front() { assert(n > 0); return data_[0]; }
229 CUDA const value_type& front() const { assert(n > 0); return data_[0]; }
230 CUDA value_type& back() { assert(n > 0); return data_[n-1]; }
231 CUDA const value_type& back() const { assert(n > 0); return data_[n-1]; }
232 CUDA value_type* data() { return data_; }
233 CUDA const value_type* data() const { return data_; }
234 CUDA bool empty() const { return n == 0; }
235 CUDA size_t size() const { return n; }
236 CUDA void reserve(size_t new_cap) { if(new_cap > cap) reallocate(new_cap); }
237 CUDA size_t capacity() const { return cap; }
238 CUDA void shrink_to_fit() { if(cap > n) reallocate(n); }
239
240 CUDA NI void clear() {
241 size_t n2 = n;
242 n = 0;
243 for(size_t i = 0; i < n2; ++i) {
244 data_[i].~T();
245 }
246 }
247
248 CUDA void push_back(const T& value) {
249 reallocate();
250 inplace_new(n, value);
251 ++n;
252 }
253
254 CUDA void push_back(T&& value) {
255 reallocate();
256 new(&data_[n]) T(std::forward<T>(value));
257 ++n;
258 }
259
260 template<class... Args>
261 CUDA value_type& emplace_back(Args&&... args) {
262 reallocate();
263 if constexpr(std::is_constructible<value_type, Args&&..., const allocator_type&>{}) {
264 new(&data_[n]) value_type(std::forward<Args>(args)..., allocator);
265 }
266 else {
267 new(&data_[n]) value_type(std::forward<Args>(args)...);
268 }
269 ++n;
270 return data_[n];
271 }
272
273 CUDA void pop_back() {
274 assert(n > 0);
275 data_[n-1].~T();
276 --n;
277 }
278
279 CUDA NI void resize(size_t count) {
280 if(count < n) {
281 for(size_t i = count; i < n; ++i) {
282 data_[i].~T();
283 }
284 n = count;
285 }
286 else {
287 if(count > cap) {
288 reallocate(count);
289 }
290 for(size_t i = n; i < count; ++i) {
291 inplace_new(i);
292 }
293 n = count;
294 }
295 }
296
297 CUDA void swap(this_type& other) {
298 ::battery::swap(n, other.n);
299 ::battery::swap(cap, other.cap);
300 ::battery::swap(data_, other.data_);
301 ::battery::swap(allocator, other.allocator);
302 }
303
304 CUDA NI void print() const {
305 for(size_t i = 0; i < n; ++i) {
306 ::battery::print(data_[i]);
307 if(i < n - 1) {
308 printf(", ");
309 }
310 }
311 }
312};
313
314template<class T1, class Alloc1, class T2, class Alloc2>
316 if(lhs.size() != rhs.size()) {
317 return false;
318 }
319 else {
320 for(size_t i = 0; i < lhs.size(); ++i) {
321 if(!(lhs[i] == rhs[i])) {
322 return false;
323 }
324 }
325 }
326 return true;
327}
328
329template<class T1, class Alloc1, class T2, class Alloc2>
330CUDA bool operator!=(const vector<T1, Alloc1>& lhs, const vector<T2, Alloc2>& rhs) {
331 return !(lhs == rhs);
332}
333
334
335} // namespace battery
336
337#endif
Definition vector.hpp:20
CUDA size_t capacity() const
Definition vector.hpp:237
CUDA void push_back(const T &value)
Definition vector.hpp:248
CUDA value_type & front()
Definition vector.hpp:228
CUDA void reserve(size_t new_cap)
Definition vector.hpp:236
CUDA NI vector(size_t n, const allocator_type &alloc=allocator_type())
Definition vector.hpp:99
Allocator allocator_type
Definition vector.hpp:24
CUDA const value_type & front() const
Definition vector.hpp:229
CUDA value_type & emplace_back(Args &&... args)
Definition vector.hpp:261
CUDA this_type & operator=(this_type &&other)
Definition vector.hpp:181
CUDA vector(const vector< U, allocator_type > &from)
Definition vector.hpp:130
CUDA value_type & operator[](size_t i)
Definition vector.hpp:218
CUDA NI vector(std::initializer_list< T > init, const allocator_type &alloc=allocator_type{})
Definition vector.hpp:152
vector(const std::vector< U, Alloc2 > &other, const allocator_type &alloc=allocator_type{})
Definition vector.hpp:163
CUDA vector(const vector< U, Allocator2 > &from, const allocator_type &alloc=allocator_type{})
Definition vector.hpp:125
CUDA NI ~vector()
Definition vector.hpp:171
CUDA void shrink_to_fit()
Definition vector.hpp:238
T value_type
Definition vector.hpp:23
CUDA void swap(this_type &other)
Definition vector.hpp:297
CUDA NI vector(this_type &&other)
Definition vector.hpp:148
CUDA NI void resize(size_t count)
Definition vector.hpp:279
CUDA vector(const this_type &from)
Definition vector.hpp:134
CUDA NI vector(size_t n, const U &from, const allocator_type &alloc=allocator_type{})
Definition vector.hpp:140
CUDA const value_type * data() const
Definition vector.hpp:233
CUDA NI this_type & operator=(const vector< U, Allocator2 > &other)
Definition vector.hpp:210
vector< value_type, allocator_type > this_type
Definition vector.hpp:25
CUDA allocator_type get_allocator() const
Definition vector.hpp:214
CUDA void pop_back()
Definition vector.hpp:273
CUDA void push_back(T &&value)
Definition vector.hpp:254
CUDA NI void print() const
Definition vector.hpp:304
CUDA NI void clear()
Definition vector.hpp:240
CUDA const value_type & operator[](size_t i) const
Definition vector.hpp:223
CUDA bool empty() const
Definition vector.hpp:234
CUDA size_t size() const
Definition vector.hpp:235
CUDA NI vector(const U *from, size_t n, const allocator_type &alloc=allocator_type{})
Definition vector.hpp:115
CUDA vector(const allocator_type &alloc=allocator_type{})
Definition vector.hpp:108
CUDA NI this_type & operator=(const this_type &other)
Definition vector.hpp:204
CUDA value_type & back()
Definition vector.hpp:230
CUDA const value_type & back() const
Definition vector.hpp:231
CUDA value_type * data()
Definition vector.hpp:232
Definition algorithm.hpp:10
CUDA INLINE constexpr T min(T a, T b)
Definition utility.hpp:116
CUDA INLINE constexpr T max(T a, T b)
Definition utility.hpp:128
CUDA constexpr void swap(T &a, T &b)
Definition utility.hpp:91
CUDA NI void print(const T &t)
Definition utility.hpp:707
CUDA bool operator==(const string< Alloc1 > &lhs, const string< Alloc2 > &rhs)
Definition string.hpp:110
#define CUDA
Definition utility.hpp:59
#define NI
Definition utility.hpp:62