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 const S& value() const;
75
76 /// @brief Return reference to the error value or throws bad_expected_access
77 /// if it's not available
78 /// @throws utils::bad_expected_access if success value is not available
79 E& error();
80
81 const E& error() const;
82
83 private:
84 std::variant<S, unexpected<E>> data_;
85};
86
87template <class E>
88unexpected<E>::unexpected(const E& error) : value_{error} {}
89
90template <class E>
91unexpected<E>::unexpected(E&& error) : value_{std::forward<E>(error)} {}
92
93template <class E>
94template <class... Args>
95unexpected<E>::unexpected(Args&&... args)
96 : value_(std::forward<Args>(args)...) {}
97
98template <class E>
99template <class U, class... Args>
100unexpected<E>::unexpected(std::initializer_list<U> il, Args&&... args)
101 : value_(il, std::forward<Args>(args)...) {}
102
103template <class E>
104E& unexpected<E>::error() noexcept {
105 return value_;
106}
107
108template <class E>
109const E& unexpected<E>::error() const noexcept {
110 return value_;
111}
112
113template <class S, class E>
114expected<S, E>::expected(const S& success) : data_(success) {}
115
116template <class S, class E>
117expected<S, E>::expected(S&& success) : data_(std::forward<S>(success)) {}
118
119template <class S, class E>
120expected<S, E>::expected(const unexpected<E>& error) : data_(error.error()) {}
121
122template <class S, class E>
123expected<S, E>::expected(unexpected<E>&& error)
124 : data_(std::forward<unexpected<E>>(error.error())) {}
125
126template <class S, class E>
127template <class G, typename>
128expected<S, E>::expected(const unexpected<G>& error)
129 : data_(utils::unexpected<E>(std::forward<G>(error.error()))) {}
130
131template <class S, class E>
132template <class G, typename>
133expected<S, E>::expected(unexpected<G>&& error)
134 : data_(utils::unexpected<E>(std::forward<G>(error.error()))) {}
135
136template <class S, class E>
137bool expected<S, E>::has_value() const noexcept {
138 return std::holds_alternative<S>(data_);
139}
140
141template <class S, class E>
142S& expected<S, E>::value() {
143 S* result = std::get_if<S>(&data_);
144 if (result == nullptr) {
145 throw bad_expected_access(
146 "Trying to get undefined value from utils::expected");
147 }
148 return *result;
149}
150
151template <class S, class E>
152const S& expected<S, E>::value() const {
153 const 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>
162E& expected<S, E>::error() {
163 unexpected<E>* result = std::get_if<unexpected<E>>(&data_);
164 if (result == nullptr) {
165 throw bad_expected_access(
166 "Trying to get undefined error value from utils::expected");
167 }
168 return result->error();
169}
170
171template <class S, class E>
172const E& expected<S, E>::error() const {
173 const unexpected<E>* 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
181} // namespace utils
182
183USERVER_NAMESPACE_END