userver: userver/utils/any_movable.hpp Source File
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
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 // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
138 return std::unique_ptr<HolderBase, HolderDeleter>{
139 // intended raw ctor call, sometimes casts
140 // NOLINTNEXTLINE(google-readability-casting)
141 new Holder{{&Deleter}, ValueType(std::forward<Args>(args)...)}};
142 }
143
144 static ValueType* GetIf(const AnyMovable* any) noexcept {
145 return (any && any->content_ && any->content_->deleter == &Deleter) ? &static_cast<Holder&>(*any->content_).held
146 : nullptr;
147 }
148};
149
150template <typename ValueType, typename>
151AnyMovable::AnyMovable(ValueType&& value)
152 : content_(Holder<std::decay_t<ValueType>>::Make(std::forward<ValueType>(value))) {
153 static_assert(
154 !std::is_same_v<AnyMovable*, std::decay_t<ValueType>> &&
155 !std::is_same_v<const AnyMovable*, std::decay_t<ValueType>>,
156 "AnyMovable misuse detected: trying to wrap AnyMovable* in another "
157 "AnyMovable. The pointer was probably meant to be dereferenced."
158 );
159}
160
161template <typename ValueType, typename... Args>
162AnyMovable::AnyMovable(std::in_place_type_t<ValueType> /*tag*/, Args&&... args)
163 : content_(Holder<ValueType>::Make(std::forward<Args>(args)...)) {}
164
165template <typename ValueType, typename Item, typename... Args>
166AnyMovable::AnyMovable(std::in_place_type_t<ValueType> /*tag*/, std::initializer_list<Item> list, Args&&... args)
167 : content_(Holder<ValueType>::Make(list, std::forward<Args>(args)...)) {}
168
169template <typename ValueType>
170AnyMovable& AnyMovable::operator=(ValueType&& rhs) {
171 *this = AnyMovable(std::forward<ValueType>(rhs));
172 return *this;
173}
174
175template <typename ValueType, typename... Args>
176ValueType& AnyMovable::Emplace(Args&&... args) {
177 content_ = Holder<ValueType>::Make(std::forward<Args>(args)...);
178 return static_cast<Holder<ValueType>&>(*content_).held;
179}
180
181template <typename ValueType, typename Item, typename... Args>
182ValueType& AnyMovable::Emplace(std::initializer_list<Item> list, Args&&... args) {
183 content_ = Holder<ValueType>::Make(list, std::forward<Args>(args)...);
184 return static_cast<Holder<ValueType>&>(*content_).held;
185}
186
187template <typename ValueType>
188ValueType* AnyCast(AnyMovable* operand) noexcept {
189 return AnyMovable::Holder<ValueType>::GetIf(operand);
190}
191
192template <typename ValueType>
193const ValueType* AnyCast(const AnyMovable* operand) noexcept {
194 return AnyMovable::Holder<ValueType>::GetIf(operand);
195}
196
197template <typename ValueType>
198// might be requested by user
199// NOLINTNEXTLINE(readability-const-return-type)
200ValueType AnyCast(AnyMovable& operand) {
201 using NonRef = std::remove_cv_t<std::remove_reference_t<ValueType>>;
202 auto* result = AnyCast<NonRef>(&operand);
203 if (!result) throw BadAnyMovableCast();
204 return static_cast<ValueType>(*result);
205}
206
207template <typename ValueType>
208// might be requested by user
209// NOLINTNEXTLINE(readability-const-return-type)
210ValueType AnyCast(const AnyMovable& operand) {
211 using NonRef = std::remove_cv_t<std::remove_reference_t<ValueType>>;
212 auto* result = AnyCast<NonRef>(&operand);
213 if (!result) throw BadAnyMovableCast();
214 return static_cast<ValueType>(*result);
215}
216
217template <typename ValueType>
218ValueType AnyCast(AnyMovable&& operand) {
219 using NonRef = std::remove_cv_t<std::remove_reference_t<ValueType>>;
220 auto* result = AnyCast<NonRef>(&operand);
221 if (!result) throw BadAnyMovableCast();
222 return static_cast<ValueType>(std::move(*result));
223}
224
225} // namespace utils
226
227USERVER_NAMESPACE_END