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
12#include <userver/compiler/impl/lifetime.hpp>
13
14USERVER_NAMESPACE_BEGIN
15
16/// Utilities
17namespace utils {
18
19/// @ingroup userver_universal userver_containers
20///
21/// @brief Replacement for `std::any` that is not copyable. It allows to store
22/// non-copyable and even non-movable types.
23///
24/// Usage example:
25/// @snippet universal/src/utils/any_movable_test.cpp AnyMovable example usage
26class AnyMovable final {
27public:
28 /// Creates an empty `AnyMovable`
29 constexpr AnyMovable() noexcept = default;
30
31 /// `AnyMovable` is movable, but not copyable
32 AnyMovable(AnyMovable&&) noexcept = default;
33 AnyMovable& operator=(AnyMovable&&) noexcept = default;
34
35 /// Copies or moves the provided object inside the `AnyMovable`. `const`,
36 /// reference, arrays and function pointers are decayed.
37 template <typename ValueType, typename = std::enable_if_t<!std::is_same_v<AnyMovable, std::decay_t<ValueType>>>>
38 /*implicit*/ AnyMovable(ValueType&& value);
39
40 /// In-place constructs an object of the specified type
41 template <typename ValueType, typename... Args>
42 explicit AnyMovable(std::in_place_type_t<ValueType> tag, Args&&... args);
43
44 /// In-place constructs an object of the specified type
45 template <typename ValueType, typename Item, typename... Args>
46 explicit AnyMovable(std::in_place_type_t<ValueType> tag, 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) USERVER_IMPL_LIFETIME_BOUND;
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) USERVER_IMPL_LIFETIME_BOUND;
66
67private:
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 {
88public:
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(
129 std::is_same_v<ValueType, std::decay_t<ValueType>>,
130 "The requested type can't be stored in an AnyMovable"
131 );
132
133 ValueType held;
134
135 static void Deleter(HolderBase& holder) noexcept { delete &static_cast<Holder&>(holder); }
136
137 template <typename... Args>
138 static std::unique_ptr<HolderBase, HolderDeleter> Make(Args&&... args) {
139 // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
140 return std::unique_ptr<HolderBase, HolderDeleter>{
141 // intended raw ctor call, sometimes casts
142 // NOLINTNEXTLINE(google-readability-casting)
143 new Holder{{&Deleter}, ValueType(std::forward<Args>(args)...)}
144 };
145 }
146
147 static ValueType* GetIf(const AnyMovable* any) noexcept {
148 return (any && any->content_ && any->content_->deleter == &Deleter)
149 ? &static_cast<Holder&>(*any->content_).held
150 : nullptr;
151 }
152};
153
154template <typename ValueType, typename>
155AnyMovable::AnyMovable(ValueType&& value)
156 : content_(Holder<std::decay_t<ValueType>>::Make(std::forward<ValueType>(value)))
157{
158 static_assert(
159 !std::is_same_v<AnyMovable*, std::decay_t<ValueType>> &&
160 !std::is_same_v<const AnyMovable*, std::decay_t<ValueType>>,
161 "AnyMovable misuse detected: trying to wrap AnyMovable* in another "
162 "AnyMovable. The pointer was probably meant to be dereferenced."
163 );
164}
165
166template <typename ValueType, typename... Args>
167AnyMovable::AnyMovable(std::in_place_type_t<ValueType> /*tag*/, Args&&... args)
168 : content_(Holder<ValueType>::Make(std::forward<Args>(args)...))
169{}
170
171template <typename ValueType, typename Item, typename... Args>
172AnyMovable::AnyMovable(std::in_place_type_t<ValueType> /*tag*/, std::initializer_list<Item> list, Args&&... args)
173 : content_(Holder<ValueType>::Make(list, std::forward<Args>(args)...))
174{}
175
176template <typename ValueType>
177AnyMovable& AnyMovable::operator=(ValueType&& rhs) {
178 *this = AnyMovable(std::forward<ValueType>(rhs));
179 return *this;
180}
181
182template <typename ValueType, typename... Args>
183ValueType& AnyMovable::Emplace(Args&&... args) USERVER_IMPL_LIFETIME_BOUND {
184 content_ = Holder<ValueType>::Make(std::forward<Args>(args)...);
185 return static_cast<Holder<ValueType>&>(*content_).held;
186}
187
188template <typename ValueType, typename Item, typename... Args>
189ValueType& AnyMovable::Emplace(std::initializer_list<Item> list, Args&&... args) USERVER_IMPL_LIFETIME_BOUND {
190 content_ = Holder<ValueType>::Make(list, std::forward<Args>(args)...);
191 return static_cast<Holder<ValueType>&>(*content_).held;
192}
193
194template <typename ValueType>
195ValueType* AnyCast(AnyMovable* operand) noexcept {
196 return AnyMovable::Holder<ValueType>::GetIf(operand);
197}
198
199template <typename ValueType>
200const ValueType* AnyCast(const AnyMovable* operand) noexcept {
201 return AnyMovable::Holder<ValueType>::GetIf(operand);
202}
203
204template <typename ValueType>
205// might be requested by user
206// NOLINTNEXTLINE(readability-const-return-type)
207ValueType AnyCast(AnyMovable& operand) {
208 using NonRef = std::remove_cv_t<std::remove_reference_t<ValueType>>;
209 auto* result = AnyCast<NonRef>(&operand);
210 if (!result) {
211 throw BadAnyMovableCast();
212 }
213 return static_cast<ValueType>(*result);
214}
215
216template <typename ValueType>
217// might be requested by user
218// NOLINTNEXTLINE(readability-const-return-type)
219ValueType AnyCast(const AnyMovable& operand) {
220 using NonRef = std::remove_cv_t<std::remove_reference_t<ValueType>>;
221 auto* result = AnyCast<NonRef>(&operand);
222 if (!result) {
223 throw BadAnyMovableCast();
224 }
225 return static_cast<ValueType>(*result);
226}
227
228template <typename ValueType>
229ValueType AnyCast(AnyMovable&& operand) {
230 using NonRef = std::remove_cv_t<std::remove_reference_t<ValueType>>;
231 auto* result = AnyCast<NonRef>(&operand);
232 if (!result) {
233 throw BadAnyMovableCast();
234 }
235 return static_cast<ValueType>(std::move(*result));
236}
237
238} // namespace utils
239
240USERVER_NAMESPACE_END