userver: userver/utils/expected.hpp Source File
Loading...
Searching...
No Matches
expected.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/utils/expected.hpp
4/// @brief @copybrief utils::expected
5
6#include <stdexcept>
7#include <string>
8#include <variant>
9
10USERVER_NAMESPACE_BEGIN
11
12namespace utils {
13
14// NOLINTBEGIN(readability-identifier-naming)
15
16class bad_expected_access : public std::exception {
17public:
18 using std::exception::exception;
19
20 explicit bad_expected_access(const std::string& message)
21 : message_{message}
22 {}
23
24 const char* what() const noexcept override { return message_.c_str(); }
25
26private:
27 std::string message_;
28};
29
30template <class E>
32public:
33 unexpected(const E& error);
34 unexpected(E&& error);
35
36 template <class... Args>
37 unexpected(Args&&... args);
38
39 template <class U, class... Args>
40 unexpected(std::initializer_list<U> il, Args&&... args);
41
42 E& error() noexcept;
43 const E& error() const noexcept;
44
45private:
46 E value_;
47};
48
49template <class E>
50unexpected(E) -> unexpected<E>;
51
52/// @ingroup userver_universal userver_containers
53///
54/// @brief For holding a value or an error
55template <class S, class E>
56class [[nodiscard]] expected {
57public:
58 constexpr expected();
59 expected(const S& success);
60 expected(S&& success);
61 expected(const unexpected<E>& error);
62 expected(unexpected<E>&& error);
63
64 template <class G, typename = std::enable_if_t<std::is_convertible_v<G, E>>>
65 expected(const unexpected<G>& error);
66
67 template <class G, typename = std::enable_if_t<std::is_convertible_v<G, E>>>
68 expected(unexpected<G>&& error);
69
70 /// @brief Check whether *this contains an expected value
71 bool has_value() const noexcept;
72
73 /// @brief Check whether *this contains an expected value
74 explicit operator bool() const noexcept;
75
76 /// @brief Return reference to the value or throws bad_expected_access
77 /// if it's not available
78 /// @throws utils::bad_expected_access if *this contain an unexpected value
79 S& value() &;
80
81 /// @overload
82 S&& value() &&;
83
84 /// @overload
85 const S& value() const&;
86
87 /// @brief Return reference to the error value or throws bad_expected_access
88 /// if it's not available
89 /// @throws utils::bad_expected_access if success value is not available
90 E& error();
91
92 /// @overload
93 const E& error() const;
94
95private:
96 std::variant<S, unexpected<E>> data_;
97};
98
99template <class E>
100class [[nodiscard]] expected<void, E> {
101public:
102 constexpr expected() noexcept;
103 expected(const unexpected<E>& error);
104 expected(unexpected<E>&& error);
105
106 template <class G, typename = std::enable_if_t<std::is_convertible_v<G, E>>>
107 expected(const unexpected<G>& error);
108
109 template <class G, typename = std::enable_if_t<std::is_convertible_v<G, E>>>
110 expected(unexpected<G>&& error);
111
112 bool has_value() const noexcept;
113 explicit operator bool() const noexcept;
114 void value() const;
115
116 E& error();
117 const E& error() const;
118
119private:
120 std::variant<std::monostate, unexpected<E>> data_;
121};
122
123template <class E>
124unexpected<E>::unexpected(const E& error)
125 : value_{error}
126{}
127
128template <class E>
129unexpected<E>::unexpected(E&& error)
130 : value_{std::forward<E>(error)}
131{}
132
133template <class E>
134template <class... Args>
135unexpected<E>::unexpected(Args&&... args)
136 : value_(std::forward<Args>(args)...)
137{}
138
139template <class E>
140template <class U, class... Args>
141unexpected<E>::unexpected(std::initializer_list<U> il, Args&&... args)
142 : value_(il, std::forward<Args>(args)...)
143{}
144
145template <class E>
146E& unexpected<E>::error() noexcept {
147 return value_;
148}
149
150template <class E>
151const E& unexpected<E>::error() const noexcept {
152 return value_;
153}
154
155template <class S, class E>
156constexpr expected<S, E>::expected()
157 : data_(std::in_place_index<0>)
158{}
159
160template <class S, class E>
161expected<S, E>::expected(const S& success)
162 : data_(success)
163{}
164
165template <class S, class E>
166expected<S, E>::expected(S&& success)
167 : data_(std::forward<S>(success))
168{}
169
170template <class S, class E>
171expected<S, E>::expected(const unexpected<E>& error)
172 : data_(error.error())
173{}
174
175template <class S, class E>
176expected<S, E>::expected(unexpected<E>&& error)
177 : data_(std::forward<unexpected<E>>(error.error()))
178{}
179
180template <class S, class E>
181template <class G, typename>
182expected<S, E>::expected(const unexpected<G>& error)
183 : data_(utils::unexpected<E>(std::forward<G>(error.error())))
184{}
185
186template <class S, class E>
187template <class G, typename>
188expected<S, E>::expected(unexpected<G>&& error)
189 : data_(utils::unexpected<E>(std::forward<G>(error.error())))
190{}
191
192template <class S, class E>
193bool expected<S, E>::has_value() const noexcept {
194 return std::holds_alternative<S>(data_);
195}
196
197template <class S, class E>
198expected<S, E>::operator bool() const noexcept {
199 return has_value();
200}
201
202template <class S, class E>
203S& expected<S, E>::value() & {
204 S* result = std::get_if<S>(&data_);
205 if (result == nullptr) {
206 throw bad_expected_access("Trying to get undefined value from utils::expected");
207 }
208 return *result;
209}
210
211template <class S, class E>
212S&& expected<S, E>::value() && {
213 return std::move(value());
214}
215
216template <class S, class E>
217const S& expected<S, E>::value() const& {
218 const S* result = std::get_if<S>(&data_);
219 if (result == nullptr) {
220 throw bad_expected_access("Trying to get undefined value from utils::expected");
221 }
222 return *result;
223}
224
225template <class S, class E>
226E& expected<S, E>::error() {
227 auto* result = std::get_if<unexpected<E>>(&data_);
228 if (result == nullptr) {
229 throw bad_expected_access("Trying to get undefined error value from utils::expected");
230 }
231 return result->error();
232}
233
234template <class S, class E>
235const E& expected<S, E>::error() const {
236 const auto* result = std::get_if<unexpected<E>>(&data_);
237 if (result == nullptr) {
238 throw bad_expected_access("Trying to get undefined error value from utils::expected");
239 }
240 return result->error();
241}
242
243template <class E>
244constexpr expected<void, E>::expected() noexcept: data_(std::in_place_index<0>) {}
245
246template <class E>
247expected<void, E>::expected(const unexpected<E>& error)
248 : data_(error.error())
249{}
250
251template <class E>
252expected<void, E>::expected(unexpected<E>&& error)
253 : data_(std::forward<unexpected<E>>(error.error()))
254{}
255
256template <class E>
257template <class G, typename>
258expected<void, E>::expected(const unexpected<G>& error)
259 : data_(utils::unexpected<E>(std::forward<G>(error.error())))
260{}
261
262template <class E>
263template <class G, typename>
264expected<void, E>::expected(unexpected<G>&& error)
265 : data_(utils::unexpected<E>(std::forward<G>(error.error())))
266{}
267
268template <class E>
269bool expected<void, E>::has_value() const noexcept {
270 return data_.index() == 0;
271}
272
273template <class E>
274expected<void, E>::operator bool() const noexcept {
275 return has_value();
276}
277
278template <class E>
279void expected<void, E>::value() const {
280 if (!has_value()) {
281 throw bad_expected_access("Trying to get undefined value from utils::expected");
282 }
283}
284
285template <class E>
286E& expected<void, E>::error() {
287 auto* result = std::get_if<unexpected<E>>(&data_);
288 if (result == nullptr) {
289 throw bad_expected_access("Trying to get undefined error value from utils::expected");
290 }
291 return result->error();
292}
293
294template <class E>
295const E& expected<void, E>::error() const {
296 const auto* result = std::get_if<unexpected<E>>(&data_);
297 if (result == nullptr) {
298 throw bad_expected_access("Trying to get undefined error value from utils::expected");
299 }
300 return result->error();
301}
302
303// NOLINTEND(readability-identifier-naming)
304
305} // namespace utils
306
307USERVER_NAMESPACE_END