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 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