Skip to content

Commit bf8848b

Browse files
falucocodebot
authored andcommitted
adt: use the smallest possible type to encode the vector size as a function of its capacity
F
1 parent 72f565c commit bf8848b

File tree

2 files changed

+51
-41
lines changed

2 files changed

+51
-41
lines changed

include/srsran/adt/static_vector.h

Lines changed: 41 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,31 @@
1414
#include "srsran/support/srsran_assert.h"
1515
#include <array>
1616
#include <iterator>
17+
#include <limits>
1718
#include <memory>
1819

1920
namespace srsran {
2021

2122
namespace detail {
2223

24+
/// Selects the smallest unsigned integer type that can represent the range [0, N].
25+
template <size_t N>
26+
using size_type_selector = std::conditional_t<
27+
(N < std::numeric_limits<uint8_t>::max()),
28+
uint8_t,
29+
std::conditional_t<
30+
(N < std::numeric_limits<uint16_t>::max()),
31+
uint16_t,
32+
std::conditional_t<(N < std::numeric_limits<uint32_t>::max()),
33+
uint32_t,
34+
std::conditional_t<(N < std::numeric_limits<uint64_t>::max()), uint64_t, size_t>>>>;
35+
2336
template <typename T, size_t Capacity>
2437
struct trivial_storage {
25-
static_assert(std::is_trivial<T>::value, "Implementation only works for trivial types");
38+
static_assert(std::is_trivial_v<T>, "Implementation only works for trivial types");
2639

27-
using array_type = std::conditional_t<std::is_const<T>::value,
28-
const std::array<std::remove_const_t<T>, Capacity>,
29-
std::array<T, Capacity>>;
40+
using array_type = std::
41+
conditional_t<std::is_const_v<T>, const std::array<std::remove_const_t<T>, Capacity>, std::array<T, Capacity>>;
3042
using iterator = T*;
3143
using const_iterator = const T*;
3244

@@ -89,16 +101,16 @@ struct trivial_storage {
89101
// do nothing.
90102
}
91103

92-
array_type array;
93-
size_t sz = 0;
104+
array_type array;
105+
size_type_selector<Capacity> sz = 0;
94106
};
95107

96108
template <typename T, size_t Capacity>
97109
struct non_trivial_storage {
98-
static_assert(not std::is_trivial<T>::value, "Implementation only works for non-trivial types");
110+
static_assert(not std::is_trivial_v<T>, "Implementation only works for non-trivial types");
99111

100112
using element_storage_t = std::aligned_storage_t<sizeof(T), alignof(T)>;
101-
using array_type = std::conditional_t<std::is_const<T>::value,
113+
using array_type = std::conditional_t<std::is_const_v<T>,
102114
const std::array<element_storage_t, Capacity>,
103115
std::array<element_storage_t, Capacity>>;
104116
using iterator = T*;
@@ -148,7 +160,7 @@ struct non_trivial_storage {
148160
const_iterator end() const noexcept { return begin() + sz; }
149161

150162
template <typename... Args>
151-
constexpr void construct_(size_t idx, Args&&... args) noexcept(std::is_nothrow_constructible<T, Args...>::value)
163+
constexpr void construct_(size_t idx, Args&&... args) noexcept(std::is_nothrow_constructible_v<T, Args...>)
152164
{
153165
static_assert(std::is_constructible<T, Args...>::value, "T(Args...) does not exist");
154166
new (&array[idx]) T(std::forward<Args>(args)...);
@@ -157,13 +169,13 @@ struct non_trivial_storage {
157169
void destroy_(size_t idx) noexcept { (*this)[idx].~T(); }
158170
void destroy_(iterator it) noexcept { (*it).~T(); }
159171

160-
array_type array;
161-
size_t sz = 0;
172+
array_type array;
173+
size_type_selector<Capacity> sz = 0;
162174
};
163175

164176
template <typename T, size_t Capacity>
165177
using static_vector_storage =
166-
std::conditional_t<std::is_trivial<T>::value, trivial_storage<T, Capacity>, non_trivial_storage<T, Capacity>>;
178+
std::conditional_t<std::is_trivial_v<T>, trivial_storage<T, Capacity>, non_trivial_storage<T, Capacity>>;
167179

168180
} // namespace detail
169181

@@ -173,7 +185,7 @@ using static_vector_storage =
173185
/// @tparam T type of the elements
174186
/// @tparam MAX_N Maximum number of elements of the static_vector
175187
template <typename T, std::size_t MAX_N>
176-
class static_vector : private detail::static_vector_storage<T, MAX_N>
188+
class static_vector : public detail::static_vector_storage<T, MAX_N>
177189
{
178190
using base_type = detail::static_vector_storage<T, MAX_N>;
179191

@@ -197,7 +209,7 @@ class static_vector : private detail::static_vector_storage<T, MAX_N>
197209
///
198210
/// Note: The ctor needs to be user-defined to forbid zero-initialization of the storage via = {}.
199211
constexpr static_vector() noexcept {}
200-
explicit constexpr static_vector(size_type count) noexcept(std::is_nothrow_default_constructible<T>::value)
212+
explicit constexpr static_vector(size_type count) noexcept(std::is_nothrow_default_constructible_v<T>)
201213
{
202214
static_assert(std::is_default_constructible<T>::value, "T must be default-constructible");
203215
srsran_assert(count <= MAX_N, "static vector maximum size={} was exceeded", MAX_N);
@@ -208,18 +220,17 @@ class static_vector : private detail::static_vector_storage<T, MAX_N>
208220
}
209221
this->sz = count;
210222
}
211-
constexpr static_vector(size_type count,
212-
const T& initial_value) noexcept(std::is_nothrow_copy_constructible<T>::value)
223+
constexpr static_vector(size_type count, const T& initial_value) noexcept(std::is_nothrow_copy_constructible_v<T>)
213224
{
214225
static_assert(std::is_copy_constructible<T>::value, "T must be copy-constructible");
215226
assign_unsafe_(count, initial_value);
216227
}
217-
constexpr static_vector(const static_vector& other) noexcept(std::is_nothrow_copy_constructible<T>::value)
228+
constexpr static_vector(const static_vector& other) noexcept(std::is_nothrow_copy_constructible_v<T>)
218229
{
219230
static_assert(std::is_copy_constructible<T>::value, "T must be copyable");
220231
assign_unsafe_(other.begin(), other.end());
221232
}
222-
constexpr static_vector(static_vector&& other) noexcept(std::is_nothrow_move_constructible<value_type>::value)
233+
constexpr static_vector(static_vector&& other) noexcept(std::is_nothrow_move_constructible_v<value_type>)
223234
{
224235
static_assert(std::is_move_constructible<T>::value, "T must be move-constructible");
225236
assign_unsafe_(std::make_move_iterator(other.begin()), std::make_move_iterator(other.end()));
@@ -233,14 +244,14 @@ class static_vector : private detail::static_vector_storage<T, MAX_N>
233244
}
234245
constexpr static_vector(std::initializer_list<T> init) : static_vector(init.begin(), init.end()) {}
235246
~static_vector() = default;
236-
constexpr static_vector& operator=(const static_vector& other) noexcept(std::is_nothrow_copy_assignable<T>::value)
247+
constexpr static_vector& operator=(const static_vector& other) noexcept(std::is_nothrow_copy_assignable_v<T>)
237248
{
238249
if (this != &other) {
239250
assign(other.begin(), other.end());
240251
}
241252
return *this;
242253
}
243-
constexpr static_vector& operator=(static_vector&& other) noexcept(std::is_nothrow_move_assignable<value_type>::value)
254+
constexpr static_vector& operator=(static_vector&& other) noexcept(std::is_nothrow_move_assignable_v<value_type>)
244255
{
245256
size_t min_common_size = std::min(other.size(), size());
246257
if (min_common_size > 0) {
@@ -266,21 +277,21 @@ class static_vector : private detail::static_vector_storage<T, MAX_N>
266277

267278
// modifiers
268279
template <typename... Args>
269-
constexpr T& emplace_back(Args&&... args) noexcept(std::is_nothrow_constructible<T, Args...>::value)
280+
constexpr T& emplace_back(Args&&... args) noexcept(std::is_nothrow_constructible_v<T, Args...>)
270281
{
271282
srsran_assert(size() < MAX_N, "Cannot emplace back in full vector");
272283
this->construct_(size(), std::forward<Args>(args)...);
273284
this->sz++;
274285
return this->back();
275286
}
276287

277-
constexpr void push_back(const T& value) noexcept(std::is_nothrow_copy_constructible<T>::value)
288+
constexpr void push_back(const T& value) noexcept(std::is_nothrow_copy_constructible_v<T>)
278289
{
279290
static_assert(std::is_copy_constructible<T>::value, "T must be copy-constructible");
280291
emplace_back(value);
281292
}
282293

283-
constexpr void push_back(T&& value) noexcept(std::is_nothrow_move_constructible<T>::value)
294+
constexpr void push_back(T&& value) noexcept(std::is_nothrow_move_constructible_v<T>)
284295
{
285296
static_assert(std::is_move_constructible<T>::value, "T must be move-constructible");
286297
emplace_back(std::move(value));
@@ -331,7 +342,7 @@ class static_vector : private detail::static_vector_storage<T, MAX_N>
331342
this->sz = new_size;
332343
}
333344

334-
constexpr iterator erase(iterator pos) noexcept(std::is_move_assignable<T>::value)
345+
constexpr iterator erase(iterator pos) noexcept(std::is_move_assignable_v<T>)
335346
{
336347
srsran_assert(pos >= this->begin() and pos < this->end(), "Iterator to erase is out-of-bounds");
337348
iterator ret = pos;
@@ -340,7 +351,7 @@ class static_vector : private detail::static_vector_storage<T, MAX_N>
340351
return ret;
341352
}
342353

343-
constexpr iterator erase(iterator it_start, iterator it_end) noexcept(std::is_move_assignable<T>::value)
354+
constexpr iterator erase(iterator it_start, iterator it_end) noexcept(std::is_move_assignable_v<T>)
344355
{
345356
srsran_assert(it_start <= it_end, "Trying to erase invalid range");
346357
srsran_assert(it_start >= begin() and it_end <= end(), "Range to erase is out-of-bounds");
@@ -353,20 +364,20 @@ class static_vector : private detail::static_vector_storage<T, MAX_N>
353364
return ret;
354365
}
355366

356-
constexpr void assign(size_type nof_elems, const T& value) noexcept(std::is_nothrow_copy_constructible<T>::value)
367+
constexpr void assign(size_type nof_elems, const T& value) noexcept(std::is_nothrow_copy_constructible_v<T>)
357368
{
358369
clear();
359370
assign_unsafe_(nof_elems, value);
360371
}
361372

362373
template <typename Iterator>
363-
constexpr void assign(Iterator it_start, Iterator it_end) noexcept(std::is_nothrow_copy_constructible<T>::value)
374+
constexpr void assign(Iterator it_start, Iterator it_end) noexcept(std::is_nothrow_copy_constructible_v<T>)
364375
{
365376
clear();
366377
assign_unsafe_(it_start, it_end);
367378
}
368379

369-
constexpr void assign(std::initializer_list<T> ilist) noexcept(std::is_nothrow_copy_constructible<T>::value)
380+
constexpr void assign(std::initializer_list<T> ilist) noexcept(std::is_nothrow_copy_constructible_v<T>)
370381
{
371382
assign(ilist.begin(), ilist.end());
372383
}
@@ -404,7 +415,7 @@ class static_vector : private detail::static_vector_storage<T, MAX_N>
404415
this->destroy_(it);
405416
}
406417
}
407-
constexpr void assign_unsafe_(size_t N, const T& val) noexcept(std::is_nothrow_copy_constructible<T>::value)
418+
constexpr void assign_unsafe_(size_t N, const T& val) noexcept(std::is_nothrow_copy_constructible_v<T>)
408419
{
409420
srsran_assert(N <= MAX_N, "static vector maximum size={} was exceeded", MAX_N);
410421
// Note: Gcc 11 fails compilation without this hint. Potentially a bug.
@@ -415,8 +426,7 @@ class static_vector : private detail::static_vector_storage<T, MAX_N>
415426
this->sz = N;
416427
}
417428
template <typename Iterator>
418-
constexpr void assign_unsafe_(Iterator it_begin,
419-
Iterator it_end) noexcept(std::is_nothrow_copy_constructible<T>::value)
429+
constexpr void assign_unsafe_(Iterator it_begin, Iterator it_end) noexcept(std::is_nothrow_copy_constructible_v<T>)
420430
{
421431
size_type N = std::distance(it_begin, it_end);
422432
srsran_assert(N <= MAX_N, "static vector maximum size={} was exceeded", MAX_N);

tests/unittests/adt/static_vector_test.cpp

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,18 @@
2121

2222
using namespace srsran;
2323

24-
static_assert(std::is_same<static_vector<int, 5>::value_type, int>::value, "Invalid traits");
25-
static_assert(std::is_trivially_destructible<static_vector<int, 5>>::value, "Invalid traits");
26-
static_assert(std::is_default_constructible<static_vector<int, 5>>::value, "Invalid traits");
27-
static_assert(not std::is_trivially_destructible<static_vector<moveonly_test_object, 5>>::value, "Invalid traits");
28-
static_assert(std::is_default_constructible<static_vector<moveonly_test_object, 5>>::value, "Invalid traits");
29-
static_assert(std::is_default_constructible<static_vector<nondefault_ctor_test_object, 5>>::value, "Invalid traits");
24+
static_assert(std::is_same_v<static_vector<int, 5>::value_type, int>, "Invalid traits");
25+
static_assert(std::is_trivially_destructible_v<static_vector<int, 5>>, "Invalid traits");
26+
static_assert(std::is_default_constructible_v<static_vector<int, 5>>, "Invalid traits");
27+
static_assert(not std::is_trivially_destructible_v<static_vector<moveonly_test_object, 5>>, "Invalid traits");
28+
static_assert(std::is_default_constructible_v<static_vector<moveonly_test_object, 5>>, "Invalid traits");
29+
static_assert(std::is_default_constructible_v<static_vector<nondefault_ctor_test_object, 5>>, "Invalid traits");
3030

3131
std::vector<int> create_test_vector(size_t sz)
3232
{
3333
std::vector<int> v(sz);
34-
for (unsigned i = 0; i != v.size(); ++i) {
35-
v[i] = test_rgen::uniform_int<int>();
34+
for (int& i : v) {
35+
i = test_rgen::uniform_int<int>();
3636
}
3737
return v;
3838
}
@@ -165,8 +165,8 @@ TYPED_TEST(static_vector_tester, push_back_creates_new_element_at_the_back)
165165
std::vector<int> expected = create_test_vector(test_rgen::uniform_int<size_t>(0, 10));
166166
static_vector<T, 10> vec;
167167

168-
for (unsigned i = 0; i != expected.size(); ++i) {
169-
vec.push_back(T(expected[i]));
168+
for (int i : expected) {
169+
vec.push_back(T(i));
170170
}
171171

172172
ASSERT_EQ(vec.size(), expected.size());

0 commit comments

Comments
 (0)