userver: userver/utils/any_movable.hpp Source File
Loading...
Searching...
No Matches
any_movable.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/utils/any_movable.hpp
4/// @brief @copybrief utils::AnyMovable
5
6#include <any> // for std::bad_any_cast
7#include <initializer_list>
8#include <memory>
9#include <type_traits>
10#include <utility>
11
12USERVER_NAMESPACE_BEGIN
13
14/// Utilities
15namespace utils {
16
17/// @ingroup userver_universal userver_containers
18///
19/// @brief Replacement for `std::any` that is not copyable. It allows to store
20/// non-copyable and even non-movable types.
21///
22/// Usage example:
23/// @snippet universal/src/utils/any_movable_test.cpp AnyMovable example usage
24class AnyMovable final {
25 public:
26 /// Creates an empty `AnyMovable`
27 constexpr AnyMovable() noexcept = default;
28
29 /// `AnyMovable` is movable, but not copyable
30 AnyMovable(AnyMovable&&) noexcept = default;
31 AnyMovable& operator=(AnyMovable&&) noexcept = default;
32
33 /// Copies or moves the provided object inside the `AnyMovable`. `const`,
34 /// reference, arrays and function pointers are decayed.
35 template <typename ValueType, typename = std::enable_if_t<!std::is_same_v<
37 /*implicit*/ AnyMovable(ValueType&& value);
38
39 /// In-place constructs an object of the specified type
40 template <typename ValueType, typename... Args>
41 explicit AnyMovable(std::in_place_type_t<ValueType> tag, Args&&... args);
42
43 /// In-place constructs an object of the specified type
44 template <typename ValueType, typename Item, typename... Args>
45 explicit AnyMovable(std::in_place_type_t<ValueType> tag,
46 std::initializer_list<Item> list, Args&&... args);
47
48 /// Copies or moves the provided object inside the `AnyMovable`. `const`,
49 /// reference, arrays and function pointers are decayed.
50 template <typename ValueType>
51 AnyMovable& operator=(ValueType&& rhs);
52
53 /// Check if the `AnyMovable` is empty
54 bool HasValue() const noexcept;
55
56 /// Destroy the old contents, making `*this` empty
57 void Reset() noexcept;
58
59 /// In-place constructs an object of the specified type
60 template <typename ValueType, typename... Args>
61 ValueType& Emplace(Args&&... args);
62
63 /// In-place constructs an object of the specified type
64 template <typename ValueType, typename Item, typename... Args>
65 ValueType& Emplace(std::initializer_list<Item> list, Args&&... args);
66
67 private:
68 struct HolderBase;
69
70 template <typename ValueType>
71 struct Holder;
72
73 struct HolderDeleter {
74 void operator()(HolderBase* holder) noexcept;
75 };
76
77 template <typename ValueType>
78 friend ValueType* AnyCast(AnyMovable*) noexcept;
79
80 template <typename ValueType>
81 friend const ValueType* AnyCast(const AnyMovable*) noexcept;
82
83 std::unique_ptr<HolderBase, HolderDeleter> content_;
84};
85
86/// @brief The exception that is thrown when `AnyCast` fails
87class BadAnyMovableCast final : public std::bad_any_cast {
88 public:
89 const char* what() const noexcept override;
90};
91
92/// @return nullptr if operand is nullptr or type of the data stored in operand
93/// does not match ValueType
94template <typename ValueType>
95ValueType* AnyCast(AnyMovable* operand) noexcept;
96
97/// @return nullptr if operand is nullptr or type of the data stored in operand
98/// does not match ValueType
99template <typename ValueType>
100const ValueType* AnyCast(const AnyMovable* operand) noexcept;
101
102/// @note Cast to a reference type to avoid extra copies
103/// @throw BadAnyMovableCast if type of the data stored in operand
104/// does not match ValueType
105template <typename ValueType>
106ValueType AnyCast(AnyMovable& operand);
107
108/// @note Cast to a reference type to avoid extra copies
109/// @throw BadAnyMovableCast if type of the data stored in operand
110/// does not match ValueType
111template <typename ValueType>
112ValueType AnyCast(const AnyMovable& operand);
113
114/// @note Cast to a reference type to avoid extra moves
115/// @throw BadAnyMovableCast if type of the data stored in operand
116/// does not match ValueType
117template <typename ValueType>
118ValueType AnyCast(AnyMovable&& operand);
119
120struct AnyMovable::HolderBase {
121 using DeleterType = void (*)(HolderBase&) noexcept;
122
123 DeleterType deleter;
124};
125
126template <typename ValueType>
127struct AnyMovable::Holder final : public HolderBase {
128 static_assert(std::is_same_v<ValueType, std::decay_t<ValueType>>,
129 "The requested type can't be stored in an AnyMovable");
130
131 ValueType held;
132
133 static void Deleter(HolderBase& holder) noexcept {
134 delete &static_cast<Holder&>(holder);
135 }
136
137 template <typename... Args>
138 static std::unique_ptr<HolderBase, HolderDeleter> Make(Args&&... args) {
139 return std::unique_ptr<HolderBase, HolderDeleter>{
140 // intended raw ctor call, sometimes casts
141 // NOLINTNEXTLINE(google-readability-casting)
142 new Holder{{&Deleter}, ValueType(std::forward<Args>(args)...)}};
143 }
144
145 static ValueType* GetIf(const AnyMovable* any) noexcept {
146 return (any && any->content_ && any->content_->deleter == &Deleter)
147 ? &static_cast<Holder&>(*any->content_).held
148 : nullptr;
149 }
150};
151
152template <typename ValueType, typename>
153AnyMovable::AnyMovable(ValueType&& value)
156 static_assert(
157 !std::is_same_v<AnyMovable*, std::decay_t<ValueType>> &&
158 !std::is_same_v<const AnyMovable*, std::decay_t<ValueType>>,
159 "AnyMovable misuse detected: trying to wrap AnyMovable* in another "
160 "AnyMovable. The pointer was probably meant to be dereferenced.");
161}
162
163template <typename ValueType, typename... Args>
164AnyMovable::AnyMovable(std::in_place_type_t<ValueType> /*tag*/, Args&&... args)
166
167template <typename ValueType, typename Item, typename... Args>
168AnyMovable::AnyMovable(std::in_place_type_t<ValueType> /*tag*/,
169 std::initializer_list<Item> list, Args&&... args)
171
172template <typename ValueType>
173AnyMovable& AnyMovable::operator=(ValueType&& rhs) {
174 *this = AnyMovable(std::forward<ValueType>(rhs));
175 return *this;
176}
177
178template <typename ValueType, typename... Args>
179ValueType& AnyMovable::Emplace(Args&&... args) {
180 content_ = Holder<ValueType>::Make(std::forward<Args>(args)...);
181 return static_cast<Holder<ValueType>&>(*content_).held;
182}
183
184template <typename ValueType, typename Item, typename... Args>
185ValueType& AnyMovable::Emplace(std::initializer_list<Item> list,
186 Args&&... args) {
187 content_ = Holder<ValueType>::Make(list, std::forward<Args>(args)...);
188 return static_cast<Holder<ValueType>&>(*content_).held;
189}
190
191template <typename ValueType>
192ValueType* AnyCast(AnyMovable* operand) noexcept {
193 return AnyMovable::Holder<ValueType>::GetIf(operand);
194}
195
196template <typename ValueType>
197const ValueType* AnyCast(const AnyMovable* operand) noexcept {
198 return AnyMovable::Holder<ValueType>::GetIf(operand);
199}
200
201template <typename ValueType>
202// might be requested by user
203// NOLINTNEXTLINE(readability-const-return-type)
204ValueType AnyCast(AnyMovable& operand) {
205 using NonRef = std::remove_cv_t<std::remove_reference_t<ValueType>>;
206 auto* result = AnyCast<NonRef>(&operand);
207 if (!result) throw BadAnyMovableCast();
208 return static_cast<ValueType>(*result);
209}
210
211template <typename ValueType>
212// might be requested by user
213// NOLINTNEXTLINE(readability-const-return-type)
214ValueType AnyCast(const AnyMovable& operand) {
215 using NonRef = std::remove_cv_t<std::remove_reference_t<ValueType>>;
216 auto* result = AnyCast<NonRef>(&operand);
217 if (!result) throw BadAnyMovableCast();
218 return static_cast<ValueType>(*result);
219}
220
221template <typename ValueType>
222ValueType AnyCast(AnyMovable&& operand) {
223 using NonRef = std::remove_cv_t<std::remove_reference_t<ValueType>>;
224 auto* result = AnyCast<NonRef>(&operand);
225 if (!result) throw BadAnyMovableCast();
226 return static_cast<ValueType>(std::move(*result));
227}
228
229} // namespace utils
230
231USERVER_NAMESPACE_END