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 {
25public:
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<AnyMovable, std::decay_t<ValueType>>>>
36 /*implicit*/ AnyMovable(ValueType&& value);
37
38 /// In-place constructs an object of the specified type
39 template <typename ValueType, typename... Args>
40 explicit AnyMovable(std::in_place_type_t<ValueType> tag, Args&&... args);
41
42 /// In-place constructs an object of the specified type
43 template <typename ValueType, typename Item, typename... Args>
44 explicit AnyMovable(std::in_place_type_t<ValueType> tag, std::initializer_list<Item> list, Args&&... args);
45
46 /// Copies or moves the provided object inside the `AnyMovable`. `const`,
47 /// reference, arrays and function pointers are decayed.
48 template <typename ValueType>
49 AnyMovable& operator=(ValueType&& rhs);
50
51 /// Check if the `AnyMovable` is empty
52 bool HasValue() const noexcept;
53
54 /// Destroy the old contents, making `*this` empty
55 void Reset() noexcept;
56
57 /// In-place constructs an object of the specified type
58 template <typename ValueType, typename... Args>
59 ValueType& Emplace(Args&&... args);
60
61 /// In-place constructs an object of the specified type
62 template <typename ValueType, typename Item, typename... Args>
63 ValueType& Emplace(std::initializer_list<Item> list, Args&&... args);
64
65private:
66 struct HolderBase;
67
68 template <typename ValueType>
69 struct Holder;
70
71 struct HolderDeleter {
72 void operator()(HolderBase* holder) noexcept;
73 };
74
75 template <typename ValueType>
76 friend ValueType* AnyCast(AnyMovable*) noexcept;
77
78 template <typename ValueType>
79 friend const ValueType* AnyCast(const AnyMovable*) noexcept;
80
81 std::unique_ptr<HolderBase, HolderDeleter> content_;
82};
83
84/// @brief The exception that is thrown when `AnyCast` fails
85class BadAnyMovableCast final : public std::bad_any_cast {
86public:
87 const char* what() const noexcept override;
88};
89
90/// @return nullptr if operand is nullptr or type of the data stored in operand
91/// does not match ValueType
92template <typename ValueType>
93ValueType* AnyCast(AnyMovable* operand) noexcept;
94
95/// @return nullptr if operand is nullptr or type of the data stored in operand
96/// does not match ValueType
97template <typename ValueType>
98const ValueType* AnyCast(const AnyMovable* operand) noexcept;
99
100/// @note Cast to a reference type to avoid extra copies
101/// @throw BadAnyMovableCast if type of the data stored in operand
102/// does not match ValueType
103template <typename ValueType>
104ValueType AnyCast(AnyMovable& operand);
105
106/// @note Cast to a reference type to avoid extra copies
107/// @throw BadAnyMovableCast if type of the data stored in operand
108/// does not match ValueType
109template <typename ValueType>
110ValueType AnyCast(const AnyMovable& operand);
111
112/// @note Cast to a reference type to avoid extra moves
113/// @throw BadAnyMovableCast if type of the data stored in operand
114/// does not match ValueType
115template <typename ValueType>
116ValueType AnyCast(AnyMovable&& operand);
117
118struct AnyMovable::HolderBase {
119 using DeleterType = void (*)(HolderBase&) noexcept;
120
121 DeleterType deleter;
122};
123
124template <typename ValueType>
125struct AnyMovable::Holder final : public HolderBase {
126 static_assert(
127 std::is_same_v<ValueType, std::decay_t<ValueType>>,
128 "The requested type can't be stored in an AnyMovable"
129 );
130
131 ValueType held;
132
133 static void Deleter(HolderBase& holder) noexcept { delete &static_cast<Holder&>(holder); }
134
135 template <typename... Args>
136 static std::unique_ptr<HolderBase, HolderDeleter> Make(Args&&... args) {
137 return std::unique_ptr<HolderBase, HolderDeleter>{
138 // intended raw ctor call, sometimes casts
139 // NOLINTNEXTLINE(google-readability-casting)
140 new Holder{{&Deleter}, ValueType(std::forward<Args>(args)...)}};
141 }
142
143 static ValueType* GetIf(const AnyMovable* any) noexcept {
144 return (any && any->content_ && any->content_->deleter == &Deleter) ? &static_cast<Holder&>(*any->content_).held
145 : nullptr;
146 }
147};
148
149template <typename ValueType, typename>
150AnyMovable::AnyMovable(ValueType&& value)
152 static_assert(
153 !std::is_same_v<AnyMovable*, std::decay_t<ValueType>> &&
154 !std::is_same_v<const AnyMovable*, std::decay_t<ValueType>>,
155 "AnyMovable misuse detected: trying to wrap AnyMovable* in another "
156 "AnyMovable. The pointer was probably meant to be dereferenced."
157 );
158}
159
160template <typename ValueType, typename... Args>
161AnyMovable::AnyMovable(std::in_place_type_t<ValueType> /*tag*/, Args&&... args)
163
164template <typename ValueType, typename Item, typename... Args>
165AnyMovable::AnyMovable(std::in_place_type_t<ValueType> /*tag*/, std::initializer_list<Item> list, Args&&... args)
167
168template <typename ValueType>
169AnyMovable& AnyMovable::operator=(ValueType&& rhs) {
170 *this = AnyMovable(std::forward<ValueType>(rhs));
171 return *this;
172}
173
174template <typename ValueType, typename... Args>
175ValueType& AnyMovable::Emplace(Args&&... args) {
176 content_ = Holder<ValueType>::Make(std::forward<Args>(args)...);
177 return static_cast<Holder<ValueType>&>(*content_).held;
178}
179
180template <typename ValueType, typename Item, typename... Args>
181ValueType& AnyMovable::Emplace(std::initializer_list<Item> list, Args&&... args) {
182 content_ = Holder<ValueType>::Make(list, std::forward<Args>(args)...);
183 return static_cast<Holder<ValueType>&>(*content_).held;
184}
185
186template <typename ValueType>
187ValueType* AnyCast(AnyMovable* operand) noexcept {
188 return AnyMovable::Holder<ValueType>::GetIf(operand);
189}
190
191template <typename ValueType>
192const ValueType* AnyCast(const AnyMovable* operand) noexcept {
193 return AnyMovable::Holder<ValueType>::GetIf(operand);
194}
195
196template <typename ValueType>
197// might be requested by user
198// NOLINTNEXTLINE(readability-const-return-type)
199ValueType AnyCast(AnyMovable& operand) {
200 using NonRef = std::remove_cv_t<std::remove_reference_t<ValueType>>;
201 auto* result = AnyCast<NonRef>(&operand);
202 if (!result) throw BadAnyMovableCast();
203 return static_cast<ValueType>(*result);
204}
205
206template <typename ValueType>
207// might be requested by user
208// NOLINTNEXTLINE(readability-const-return-type)
209ValueType AnyCast(const AnyMovable& operand) {
210 using NonRef = std::remove_cv_t<std::remove_reference_t<ValueType>>;
211 auto* result = AnyCast<NonRef>(&operand);
212 if (!result) throw BadAnyMovableCast();
213 return static_cast<ValueType>(*result);
214}
215
216template <typename ValueType>
217ValueType AnyCast(AnyMovable&& operand) {
218 using NonRef = std::remove_cv_t<std::remove_reference_t<ValueType>>;
219 auto* result = AnyCast<NonRef>(&operand);
220 if (!result) throw BadAnyMovableCast();
221 return static_cast<ValueType>(std::move(*result));
222}
223
224} // namespace utils
225
226USERVER_NAMESPACE_END