userver: userver/utils/expected.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
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