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 void Emplace(Args&&... args);
62
63 /// In-place constructs an object of the specified type
64 template <typename ValueType, typename Item, typename... Args>
65 void 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>
179void AnyMovable::Emplace(Args&&... args) {
180 content_ = Holder<ValueType>::Make(std::forward<Args>(args)...);
181}
182
183template <typename ValueType, typename Item, typename... Args>
184void AnyMovable::Emplace(std::initializer_list<Item> list, Args&&... args) {
185 content_ = Holder<ValueType>::Make(list, std::forward<Args>(args)...);
186}
187
188template <typename ValueType>
189ValueType* AnyCast(AnyMovable* operand) noexcept {
190 return AnyMovable::Holder<ValueType>::GetIf(operand);
191}
192
193template <typename ValueType>
194const ValueType* AnyCast(const AnyMovable* operand) noexcept {
195 return AnyMovable::Holder<ValueType>::GetIf(operand);
196}
197
198template <typename ValueType>
199// might be requested by user
200// NOLINTNEXTLINE(readability-const-return-type)
201ValueType AnyCast(AnyMovable& operand) {
202 using NonRef = std::remove_cv_t<std::remove_reference_t<ValueType>>;
203 auto* result = AnyCast<NonRef>(&operand);
204 if (!result) throw BadAnyMovableCast();
205 return static_cast<ValueType>(*result);
206}
207
208template <typename ValueType>
209// might be requested by user
210// NOLINTNEXTLINE(readability-const-return-type)
211ValueType AnyCast(const AnyMovable& operand) {
212 using NonRef = std::remove_cv_t<std::remove_reference_t<ValueType>>;
213 auto* result = AnyCast<NonRef>(&operand);
214 if (!result) throw BadAnyMovableCast();
215 return static_cast<ValueType>(*result);
216}
217
218template <typename ValueType>
219ValueType AnyCast(AnyMovable&& operand) {
220 using NonRef = std::remove_cv_t<std::remove_reference_t<ValueType>>;
221 auto* result = AnyCast<NonRef>(&operand);
222 if (!result) throw BadAnyMovableCast();
223 return static_cast<ValueType>(std::move(*result));
224}
225
226} // namespace utils
227
228USERVER_NAMESPACE_END