userver: userver/utils/fixed_array.hpp Source File
Loading...
Searching...
No Matches
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 allows initializing each of the array elements with the same parameters:
23/// @snippet src/utils/fixed_array_test.cpp Sample FixedArray
24///
25/// The array also allows initializing each of the array elements with the output of a generator funtion:
26/// @snippet src/utils/fixed_array_test.cpp Sample GenerateFixedArray
27template <class T>
28class FixedArray final {
29public:
30 using iterator = T*;
31 using const_iterator = const T*;
32
33 /// Make an empty array
34 FixedArray() = default;
35
36 /// Make an array and initialize each element with "args"
37 template <class... Args>
38 explicit FixedArray(std::size_t size, Args&&... args);
39
40 FixedArray(FixedArray&& other) noexcept;
41 FixedArray& operator=(FixedArray&& other) noexcept;
42
43 FixedArray(const FixedArray&) = delete;
44 FixedArray& operator=(const FixedArray&) = delete;
45
46 ~FixedArray();
47
48 std::size_t size() const noexcept { return size_; }
49 bool empty() const noexcept { return size_ == 0; }
50
51 const T& operator[](std::size_t i) const noexcept {
52 UASSERT(i < size_);
53 return data()[i];
54 }
55
56 T& operator[](std::size_t i) noexcept {
57 UASSERT(i < size_);
58 return data()[i];
59 }
60
61 T& front() noexcept { return *NonEmptyData(); }
62 const T& front() const noexcept { return *NonEmptyData(); }
63
64 T& back() noexcept { return *(NonEmptyData() + size_ - 1); }
65 const T& back() const noexcept { return *(NonEmptyData() + size_ - 1); }
66
67 T* data() noexcept { return storage_; }
68 const T* data() const noexcept { return storage_; }
69
70 T* begin() noexcept { return data(); }
71 T* end() noexcept { return data() + size_; }
72 const T* begin() const noexcept { return data(); }
73 const T* end() const noexcept { return data() + size_; }
74 const T* cbegin() const noexcept { return data(); }
75 const T* cend() const noexcept { return data() + size_; }
76
77 /// @cond
78 template <class GeneratorFunc>
79 FixedArray(impl::InternalTag tag, std::size_t size, GeneratorFunc&& generator);
80 /// @endcond
81
82private:
83 T* NonEmptyData() noexcept {
84 UASSERT(size_);
85 return data();
86 }
87
88 const T* NonEmptyData() const noexcept {
89 UASSERT(size_);
90 return data();
91 }
92
93 T* storage_{nullptr};
94 std::size_t size_{0};
95};
96
97/// @brief Applies @p generator to indices in the range `[0, size)`, storing the
98/// results in a new utils::FixedArray. The generator is guaranteed to be invoked in the first-to-last order.
99///
100/// Usage example:
101/// @snippet src/utils/fixed_array_test.cpp Sample GenerateFixedArray
102///
103/// @param size How many objects to generate
104/// @param generator A functor that takes an index and returns an object for the
105/// `FixedArray`
106/// @returns `FixedArray` with the return objects of @p generator
107template <class GeneratorFunc>
108auto GenerateFixedArray(std::size_t size, GeneratorFunc&& generator);
109
110template <class T>
111template <class... Args>
112FixedArray<T>::FixedArray(std::size_t size, Args&&... args)
113 : size_(size)
114{
115 if (size_ == 0) {
116 return;
117 }
118 storage_ = std::allocator<T>{}.allocate(size_);
119
120 auto* begin = data();
121 try {
122 for (auto* end = begin + size - 1; begin != end; ++begin) {
123 new (begin) T(args...);
124 }
125 new (begin) T(std::forward<Args>(args)...);
126 } catch (...) {
127 std::destroy(data(), begin);
128 std::allocator<T>{}.deallocate(storage_, size);
129 throw;
130 }
131}
132
133template <class T>
134template <class GeneratorFunc>
135FixedArray<T>::FixedArray(impl::InternalTag /*tag*/, std::size_t size, GeneratorFunc&& generator)
136 : size_(size)
137{
138 if (size_ == 0) {
139 return;
140 }
141 storage_ = std::allocator<T>{}.allocate(size_);
142
143 auto* our_begin = begin();
144 auto* const our_end = end();
145 std::size_t index = 0;
146
147 try {
148 for (; our_begin != our_end; ++our_begin) {
149 new (our_begin) T(generator(index));
150 ++index;
151 }
152 } catch (...) {
153 std::destroy(begin(), our_begin);
154 std::allocator<T>{}.deallocate(storage_, size_);
155 throw;
156 }
157}
158
159template <class T>
160FixedArray<T>::FixedArray(FixedArray&& other) noexcept
161 : storage_(std::exchange(other.storage_, nullptr)), size_(std::exchange(other.size_, 0)) {}
162
163template <class T>
164FixedArray<T>& FixedArray<T>::operator=(FixedArray&& other) noexcept {
165 std::swap(storage_, other.storage_);
166 std::swap(size_, other.size_);
167 return *this;
168}
169
170template <class T>
171FixedArray<T>::~FixedArray() {
172 std::destroy(begin(), end());
173 std::allocator<T>{}.deallocate(storage_, size_);
174}
175
176template <class GeneratorFunc>
177auto GenerateFixedArray(std::size_t size, GeneratorFunc&& generator) {
178 using ResultType = std::remove_reference_t<std::invoke_result_t<GeneratorFunc&, std::size_t>>;
179 return FixedArray<ResultType>(impl::InternalTag{}, size, std::forward<GeneratorFunc>(generator));
180}
181
182} // namespace utils
183
184USERVER_NAMESPACE_END