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 {
15public:
16 using std::exception::exception;
17
18 explicit bad_expected_access(const std::string& message) : message_{message} {}
19
20 const char* what() const noexcept override { return message_.c_str(); }
21
22private:
23 std::string message_;
24};
25
26template <class E>
28public:
29 unexpected(const E& error);
30 unexpected(E&& error);
31
32 template <class... Args>
33 unexpected(Args&&... args);
34
35 template <class U, class... Args>
36 unexpected(std::initializer_list<U> il, Args&&... args);
37
38 E& error() noexcept;
39 const E& error() const noexcept;
40
41private:
42 E value_;
43};
44
45template <class E>
46unexpected(E) -> unexpected<E>;
47
48/// @ingroup userver_universal userver_containers
49///
50/// @brief For holding a value or an error
51template <class S, class E>
52class [[nodiscard]] expected {
53public:
54 constexpr expected() noexcept(std::is_void_v<S>);
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 /// @overload
75 S&& value() &&;
76
77 /// @overload
78 const S& value() const&;
79
80 /// @brief Return reference to the error value or throws bad_expected_access
81 /// if it's not available
82 /// @throws utils::bad_expected_access if success value is not available
83 E& error();
84
85 /// @overload
86 const E& error() const;
87
88private:
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) : value_(std::forward<Args>(args)...) {}
101
102template <class E>
103template <class U, class... Args>
104unexpected<E>::unexpected(std::initializer_list<U> il, Args&&... args) : value_(il, std::forward<Args>(args)...) {}
105
106template <class E>
107E& unexpected<E>::error() noexcept {
108 return value_;
109}
110
111template <class E>
112const E& unexpected<E>::error() const noexcept {
113 return value_;
114}
115
116template <class S, class E>
117constexpr expected<S, E>::expected() noexcept(std::is_void_v<S>) : data_(std::in_place_index<0>) {}
118
119template <class S, class E>
120expected<S, E>::expected(const S& success) : data_(success) {}
121
122template <class S, class E>
123expected<S, E>::expected(S&& success) : data_(std::forward<S>(success)) {}
124
125template <class S, class E>
126expected<S, E>::expected(const unexpected<E>& error) : data_(error.error()) {}
127
128template <class S, class E>
129expected<S, E>::expected(unexpected<E>&& error) : 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) : data_(utils::unexpected<E>(std::forward<G>(error.error()))) {}
134
135template <class S, class E>
136template <class G, typename>
137expected<S, E>::expected(unexpected<G>&& error) : data_(utils::unexpected<E>(std::forward<G>(error.error()))) {}
138
139template <class S, class E>
140bool expected<S, E>::has_value() const noexcept {
141 return std::holds_alternative<S>(data_);
142}
143
144template <class S, class E>
145S& expected<S, E>::value() & {
146 S* result = std::get_if<S>(&data_);
147 if (result == nullptr) {
148 throw bad_expected_access("Trying to get undefined value from utils::expected");
149 }
150 return *result;
151}
152
153template <class S, class E>
154S&& expected<S, E>::value() && {
155 return std::move(value());
156}
157
158template <class S, class E>
159const S& expected<S, E>::value() const& {
160 const S* result = std::get_if<S>(&data_);
161 if (result == nullptr) {
162 throw bad_expected_access("Trying to get undefined value from utils::expected");
163 }
164 return *result;
165}
166
167template <class S, class E>
168E& expected<S, E>::error() {
169 auto* result = std::get_if<unexpected<E>>(&data_);
170 if (result == nullptr) {
171 throw bad_expected_access("Trying to get undefined error value from utils::expected");
172 }
173 return result->error();
174}
175
176template <class S, class E>
177const E& expected<S, E>::error() const {
178 const auto* result = std::get_if<unexpected<E>>(&data_);
179 if (result == nullptr) {
180 throw bad_expected_access("Trying to get undefined error value from utils::expected");
181 }
182 return result->error();
183}
184
185} // namespace utils
186
187USERVER_NAMESPACE_END