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