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 constexpr expected() noexcept(std::is_void_v<S>);
56 expected(const S& success);
57 expected(S&& success);
58 expected(const unexpected<E>& error);
59 expected(unexpected<E>&& error);
60
61 template <class G, typename = std::enable_if_t<std::is_convertible_v<G, E>>>
62 expected(const unexpected<G>& error);
63
64 template <class G, typename = std::enable_if_t<std::is_convertible_v<G, E>>>
65 expected(unexpected<G>&& error);
66
67 /// @brief Check whether *this contains an expected value
68 bool has_value() const noexcept;
69
70 /// @brief Return reference to the value or throws bad_expected_access
71 /// if it's not available
72 /// @throws utils::bad_expected_access if *this contain an unexpected value
73 S& value() &;
74
75 /// @overload
76 S&& value() &&;
77
78 /// @overload
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 /// @overload
87 const E& error() const;
88
89 private:
90 std::variant<S, unexpected<E>> data_;
91};
92
93template <class E>
94unexpected<E>::unexpected(const E& error) : value_{error} {}
95
96template <class E>
97unexpected<E>::unexpected(E&& error) : value_{std::forward<E>(error)} {}
98
99template <class E>
100template <class... Args>
101unexpected<E>::unexpected(Args&&... args)
102 : value_(std::forward<Args>(args)...) {}
103
104template <class E>
105template <class U, class... Args>
106unexpected<E>::unexpected(std::initializer_list<U> il, Args&&... args)
107 : value_(il, std::forward<Args>(args)...) {}
108
109template <class E>
110E& unexpected<E>::error() noexcept {
111 return value_;
112}
113
114template <class E>
115const E& unexpected<E>::error() const noexcept {
116 return value_;
117}
118
119template <class S, class E>
120constexpr expected<S, E>::expected() noexcept(std::is_void_v<S>)
121 : data_(std::in_place_index<0>) {}
122
123template <class S, class E>
124expected<S, E>::expected(const S& success) : data_(success) {}
125
126template <class S, class E>
127expected<S, E>::expected(S&& success) : data_(std::forward<S>(success)) {}
128
129template <class S, class E>
130expected<S, E>::expected(const unexpected<E>& error) : data_(error.error()) {}
131
132template <class S, class E>
133expected<S, E>::expected(unexpected<E>&& error)
134 : data_(std::forward<unexpected<E>>(error.error())) {}
135
136template <class S, class E>
137template <class G, typename>
138expected<S, E>::expected(const unexpected<G>& error)
139 : data_(utils::unexpected<E>(std::forward<G>(error.error()))) {}
140
141template <class S, class E>
142template <class G, typename>
143expected<S, E>::expected(unexpected<G>&& error)
144 : data_(utils::unexpected<E>(std::forward<G>(error.error()))) {}
145
146template <class S, class E>
147bool expected<S, E>::has_value() const noexcept {
148 return std::holds_alternative<S>(data_);
149}
150
151template <class S, class E>
152S& expected<S, E>::value() & {
153 S* result = std::get_if<S>(&data_);
154 if (result == nullptr) {
155 throw bad_expected_access(
156 "Trying to get undefined value from utils::expected");
157 }
158 return *result;
159}
160
161template <class S, class E>
162S&& expected<S, E>::value() && {
163 return std::move(value());
164}
165
166template <class S, class E>
167const S& expected<S, E>::value() const& {
168 const S* result = std::get_if<S>(&data_);
169 if (result == nullptr) {
170 throw bad_expected_access(
171 "Trying to get undefined value from utils::expected");
172 }
173 return *result;
174}
175
176template <class S, class E>
177E& expected<S, E>::error() {
178 auto* result = std::get_if<unexpected<E>>(&data_);
179 if (result == nullptr) {
180 throw bad_expected_access(
181 "Trying to get undefined error value from utils::expected");
182 }
183 return result->error();
184}
185
186template <class S, class E>
187const E& expected<S, E>::error() const {
188 const auto* result = std::get_if<unexpected<E>>(&data_);
189 if (result == nullptr) {
190 throw bad_expected_access(
191 "Trying to get undefined error value from utils::expected");
192 }
193 return result->error();
194}
195
196} // namespace utils
197
198USERVER_NAMESPACE_END