userver: userver/utils/any_movable.hpp Source File
⚠️ This is the documentation for an old userver version. Click here to switch to the latest version.
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 {
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