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
14class bad_expected_access : public std::exception {
15 public:
16 using std::exception::exception;
17
18 explicit bad_expected_access(const std::string& message)
19 : message_{message} {}
20
21 const char* what() const noexcept override { return message_.c_str(); }
22
23 private:
24 std::string message_;
25};
26
27template <class E>
29 public:
30 unexpected(const E& error);
31 unexpected(E&& error);
32
33 template <class... Args>
34 unexpected(Args&&... args);
35
36 template <class U, class... Args>
37 unexpected(std::initializer_list<U> il, Args&&... args);
38
39 E& error() noexcept;
40 const E& error() const noexcept;
41
42 private:
43 E value_;
44};
45
46template <class E>
47unexpected(E) -> unexpected<E>;
48
49/// @ingroup userver_universal userver_containers
50///
51/// @brief For holding a value or an error
52template <class S, class E>
53class [[nodiscard]] expected {
54 public:
55 expected(const S& success);
56 expected(S&& success);
57 expected(const unexpected<E>& error);
58 expected(unexpected<E>&& error);
59
60 template <class G, typename = std::enable_if_t<std::is_convertible_v<G, E>>>
61 expected(const unexpected<G>& error);
62
63 template <class G, typename = std::enable_if_t<std::is_convertible_v<G, E>>>
64 expected(unexpected<G>&& error);
65
66 /// @brief Check whether *this contains an expected value
67 bool has_value() const noexcept;
68
69 /// @brief Return reference to the value or throws bad_expected_access
70 /// if it's not available
71 /// @throws utils::bad_expected_access if *this contain an unexpected value
72 S& value() &;
73
74 /// @brief Extracts the value or throws bad_expected_access
75 /// if it's not available
76 /// @throws utils::bad_expected_access if *this contain an unexpected value
77 S value() &&;
78
79 const S& value() const&;
80
81 /// @brief Return reference to the error value or throws bad_expected_access
82 /// if it's not available
83 /// @throws utils::bad_expected_access if success value is not available
84 E& error();
85
86 const E& error() const;
87
88 private:
89 std::variant<S, unexpected<E>> data_;
90};
91
92template <class E>
93unexpected<E>::unexpected(const E& error) : value_{error} {}
94
95template <class E>
96unexpected<E>::unexpected(E&& error) : value_{std::forward<E>(error)} {}
97
98template <class E>
99template <class... Args>
100unexpected<E>::unexpected(Args&&... args)
101 : value_(std::forward<Args>(args)...) {}
102
103template <class E>
104template <class U, class... Args>
105unexpected<E>::unexpected(std::initializer_list<U> il, Args&&... args)
106 : value_(il, std::forward<Args>(args)...) {}
107
108template <class E>
109E& unexpected<E>::error() noexcept {
110 return value_;
111}
112
113template <class E>
114const E& unexpected<E>::error() const noexcept {
115 return value_;
116}
117
118template <class S, class E>
119expected<S, E>::expected(const S& success) : data_(success) {}
120
121template <class S, class E>
122expected<S, E>::expected(S&& success) : data_(std::forward<S>(success)) {}
123
124template <class S, class E>
125expected<S, E>::expected(const unexpected<E>& error) : data_(error.error()) {}
126
127template <class S, class E>
128expected<S, E>::expected(unexpected<E>&& error)
129 : data_(std::forward<unexpected<E>>(error.error())) {}
130
131template <class S, class E>
132template <class G, typename>
133expected<S, E>::expected(const unexpected<G>& error)
134 : data_(utils::unexpected<E>(std::forward<G>(error.error()))) {}
135
136template <class S, class E>
137template <class G, typename>
138expected<S, E>::expected(unexpected<G>&& error)
139 : data_(utils::unexpected<E>(std::forward<G>(error.error()))) {}
140
141template <class S, class E>
142bool expected<S, E>::has_value() const noexcept {
143 return std::holds_alternative<S>(data_);
144}
145
146template <class S, class E>
147S& expected<S, E>::value() & {
148 S* result = std::get_if<S>(&data_);
149 if (result == nullptr) {
150 throw bad_expected_access(
151 "Trying to get undefined value from utils::expected");
152 }
153 return *result;
154}
155
156template <class S, class E>
157S expected<S, E>::value() && {
158 return std::move(value());
159}
160
161template <class S, class E>
162const S& expected<S, E>::value() const& {
163 const S* result = std::get_if<S>(&data_);
164 if (result == nullptr) {
165 throw bad_expected_access(
166 "Trying to get undefined value from utils::expected");
167 }
168 return *result;
169}
170
171template <class S, class E>
172E& expected<S, E>::error() {
173 auto* result = std::get_if<unexpected<E>>(&data_);
174 if (result == nullptr) {
175 throw bad_expected_access(
176 "Trying to get undefined error value from utils::expected");
177 }
178 return result->error();
179}
180
181template <class S, class E>
182const E& expected<S, E>::error() const {
183 const auto* result = std::get_if<unexpected<E>>(&data_);
184 if (result == nullptr) {
185 throw bad_expected_access(
186 "Trying to get undefined error value from utils::expected");
187 }
188 return result->error();
189}
190
191} // namespace utils
192
193USERVER_NAMESPACE_END