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) : size_(size) {
113 if (size_ == 0) return;
114 storage_ = std::allocator<T>{}.allocate(size_);
115
116 auto* begin = data();
117 try {
118 for (auto* end = begin + size - 1; begin != end; ++begin) {
119 new (begin) T(args...);
120 }
121 new (begin) T(std::forward<Args>(args)...);
122 } catch (...) {
123 std::destroy(data(), begin);
124 std::allocator<T>{}.deallocate(storage_, size);
125 throw;
126 }
127}
128
129template <class T>
130template <class GeneratorFunc>
131FixedArray<T>::FixedArray(impl::InternalTag /*tag*/, std::size_t size, GeneratorFunc&& generator) : size_(size) {
132 if (size_ == 0) return;
133 storage_ = std::allocator<T>{}.allocate(size_);
134
135 auto* our_begin = begin();
136 auto* const our_end = end();
137 std::size_t index = 0;
138
139 try {
140 for (; our_begin != our_end; ++our_begin) {
141 new (our_begin) T(generator(index));
142 ++index;
143 }
144 } catch (...) {
145 std::destroy(begin(), our_begin);
146 std::allocator<T>{}.deallocate(storage_, size_);
147 throw;
148 }
149}
150
151template <class T>
152FixedArray<T>::FixedArray(FixedArray&& other) noexcept
153 : storage_(std::exchange(other.storage_, nullptr)), size_(std::exchange(other.size_, 0)) {}
154
155template <class T>
156FixedArray<T>& FixedArray<T>::operator=(FixedArray&& other) noexcept {
157 std::swap(storage_, other.storage_);
158 std::swap(size_, other.size_);
159 return *this;
160}
161
162template <class T>
163FixedArray<T>::~FixedArray() {
164 std::destroy(begin(), end());
165 std::allocator<T>{}.deallocate(storage_, size_);
166}
167
168template <class GeneratorFunc>
169auto GenerateFixedArray(std::size_t size, GeneratorFunc&& generator) {
170 using ResultType = std::remove_reference_t<std::invoke_result_t<GeneratorFunc&, std::size_t>>;
171 return FixedArray<ResultType>(impl::InternalTag{}, size, std::forward<GeneratorFunc>(generator));
172}
173
174} // namespace utils
175
176USERVER_NAMESPACE_END