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