userver: userver/utils/fixed_array.hpp Source File
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
fixed_array.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/utils/fixed_array.hpp
4/// @brief @copybrief utils::FixedArray
5
6#include <cstddef>
7#include <memory> // std::allocator
8#include <type_traits>
9#include <utility>
10
11#include <userver/utils/assert.hpp>
12#include <userver/utils/impl/internal_tag.hpp>
13
14USERVER_NAMESPACE_BEGIN
15
16namespace utils {
17
18/// @ingroup userver_universal userver_containers
19///
20/// @brief A fixed-size array with the size determined at runtime.
21///
22/// The array also allows initializing each of the array elements with the same
23/// parameters:
24/// @snippet src/utils/fixed_array_test.cpp Sample FixedArray
25template <class T>
26class FixedArray final {
27public:
28 using iterator = T*;
29 using const_iterator = const T*;
30
31 /// Make an empty array
32 FixedArray() = default;
33
34 /// Make an array and initialize each element with "args"
35 template <class... Args>
36 explicit FixedArray(std::size_t size, Args&&... args);
37
38 FixedArray(FixedArray&& other) noexcept;
39 FixedArray& operator=(FixedArray&& other) noexcept;
40
41 FixedArray(const FixedArray&) = delete;
42 FixedArray& operator=(const FixedArray&) = delete;
43
44 ~FixedArray();
45
46 std::size_t size() const noexcept { return size_; }
47 bool empty() const noexcept { return size_ == 0; }
48
49 const T& operator[](std::size_t i) const noexcept {
50 UASSERT(i < size_);
51 return data()[i];
52 }
53
54 T& operator[](std::size_t i) noexcept {
55 UASSERT(i < size_);
56 return data()[i];
57 }
58
59 T& front() noexcept { return *NonEmptyData(); }
60 const T& front() const noexcept { return *NonEmptyData(); }
61
62 T& back() noexcept { return *(NonEmptyData() + size_ - 1); }
63 const T& back() const noexcept { return *(NonEmptyData() + size_ - 1); }
64
65 T* data() noexcept { return storage_; }
66 const T* data() const noexcept { return storage_; }
67
68 T* begin() noexcept { return data(); }
69 T* end() noexcept { return data() + size_; }
70 const T* begin() const noexcept { return data(); }
71 const T* end() const noexcept { return data() + size_; }
72 const T* cbegin() const noexcept { return data(); }
73 const T* cend() const noexcept { return data() + size_; }
74
75 /// @cond
76 template <class GeneratorFunc>
77 FixedArray(impl::InternalTag tag, std::size_t size, GeneratorFunc&& generator);
78 /// @endcond
79
80private:
81 T* NonEmptyData() noexcept {
82 UASSERT(size_);
83 return data();
84 }
85
86 const T* NonEmptyData() const noexcept {
87 UASSERT(size_);
88 return data();
89 }
90
91 T* storage_{nullptr};
92 std::size_t size_{0};
93};
94
95/// @brief Applies @p generator to indices in the range `[0, size)`, storing the
96/// results in a new utils::FixedArray. The generator is guaranteed to be
97/// invoked in the first-to-last order.
98/// @param size How many objects to generate
99/// @param generator A functor that takes an index and returns an object for the
100/// `FixedArray`
101/// @returns `FixedArray` with the return objects of @p generator
102template <class GeneratorFunc>
103auto GenerateFixedArray(std::size_t size, GeneratorFunc&& generator);
104
105template <class T>
106template <class... Args>
107FixedArray<T>::FixedArray(std::size_t size, Args&&... args) : size_(size) {
108 if (size_ == 0) return;
109 storage_ = std::allocator<T>{}.allocate(size_);
110
111 auto* begin = data();
112 try {
113 for (auto* end = begin + size - 1; begin != end; ++begin) {
114 new (begin) T(args...);
115 }
116 new (begin) T(std::forward<Args>(args)...);
117 } catch (...) {
118 std::destroy(data(), begin);
119 std::allocator<T>{}.deallocate(storage_, size);
120 throw;
121 }
122}
123
124template <class T>
125template <class GeneratorFunc>
126FixedArray<T>::FixedArray(impl::InternalTag /*tag*/, std::size_t size, GeneratorFunc&& generator) : size_(size) {
127 if (size_ == 0) return;
128 storage_ = std::allocator<T>{}.allocate(size_);
129
130 auto* our_begin = begin();
131 auto* const our_end = end();
132 std::size_t index = 0;
133
134 try {
135 for (; our_begin != our_end; ++our_begin) {
136 new (our_begin) T(generator(index));
137 ++index;
138 }
139 } catch (...) {
140 std::destroy(begin(), our_begin);
141 std::allocator<T>{}.deallocate(storage_, size_);
142 throw;
143 }
144}
145
146template <class T>
147FixedArray<T>::FixedArray(FixedArray&& other) noexcept
148 : storage_(std::exchange(other.storage_, nullptr)), size_(std::exchange(other.size_, 0)) {}
149
150template <class T>
151FixedArray<T>& FixedArray<T>::operator=(FixedArray&& other) noexcept {
152 std::swap(storage_, other.storage_);
153 std::swap(size_, other.size_);
154 return *this;
155}
156
157template <class T>
158FixedArray<T>::~FixedArray() {
159 std::destroy(begin(), end());
160 std::allocator<T>{}.deallocate(storage_, size_);
161}
162
163template <class GeneratorFunc>
164auto GenerateFixedArray(std::size_t size, GeneratorFunc&& generator) {
165 using ResultType = std::remove_reference_t<std::invoke_result_t<GeneratorFunc&, std::size_t>>;
166 return FixedArray<ResultType>(impl::InternalTag{}, size, std::forward<GeneratorFunc>(generator));
167}
168
169} // namespace utils
170
171USERVER_NAMESPACE_END