YACLib
C++ library for concurrent tasks execution
Loading...
Searching...
No Matches
result.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <yaclib/config.hpp>
4#include <yaclib/fwd.hpp>
5#include <yaclib/log.hpp>
7
8#include <exception>
9#include <new>
10#include <type_traits>
11#include <utility>
12
13namespace yaclib {
14
15// [[msvc::no_unique_address]] on the all empty union below miscompiles with MSVC
16// (runtime SEH in every Result<void> path, VS2022), so the empty value optimization
17// is disabled there and sizeof(Result<void>) stays sizeof(exception_ptr) + 8
18#if defined(_MSC_VER) && !defined(__clang__)
19# define YACLIB_RESULT_EMPTY_VALUE
20#else
21# define YACLIB_RESULT_EMPTY_VALUE YACLIB_NO_UNIQUE_ADDRESS
22#endif
23
24/**
25 * Exception that represents cancellation of an async operation, \see StopPtr
26 */
27struct StopException : std::exception {
28 const char* what() const noexcept override {
29 return "yaclib::StopException";
30 }
31};
32
33/**
34 * Cached std::exception_ptr with \ref StopException, copy is cheap and never allocates
35 */
36[[nodiscard]] const std::exception_ptr& StopPtr() noexcept;
37
38/**
39 * Check if error represents cancellation, \see StopException
40 */
41[[nodiscard]] bool IsStop(const std::exception_ptr& error) noexcept;
42
43/**
44 * Encapsulated return value from caller
45 *
46 * Either a value of type V or a std::exception_ptr.
47 * Default constructed Result contains the stop error, \see StopPtr
48 *
49 * \tparam ValueT type of value that stored in Result
50 */
53 static_assert(Check<ValueT>(), "V should be valid");
54
55 using V = std::conditional_t<std::is_void_v<ValueT>, Unit, ValueT>;
56
57 union State {
60
61 State() noexcept : stub{} {
62 }
63 ~State() noexcept {
64 }
65 };
66
67 public:
68 Result() noexcept : _error{StopPtr()} {
69 }
70
72 }
73
74 Result(std::exception_ptr error) noexcept : _error{std::move(error)} {
75 // null exception_ptr would be indistinguishable from a value, treat it as stop
76 YACLIB_ASSERT(_error != nullptr);
77 if (_error == nullptr) {
78 _error = StopPtr();
79 }
80 }
81
82 template <typename... Args>
83 explicit Result(std::in_place_t, Args&&... args) noexcept(std::is_nothrow_constructible_v<V, Args&&...>) {
84 ::new (&_value.value) V(std::forward<Args>(args)...);
85 }
86
87 template <typename... Args,
88 typename = std::enable_if_t<(sizeof...(Args) > 1) ||
89 !(std::is_same_v<std::decay_t<head_t<Args&&...>>, Result> ||
90 std::is_same_v<std::decay_t<head_t<Args&&...>>, std::exception_ptr> ||
91 std::is_same_v<std::decay_t<head_t<Args&&...>>, StopTag> ||
92 std::is_same_v<std::decay_t<head_t<Args&&...>>, std::in_place_t>)>>
93 Result(Args&&... args) noexcept(std::is_nothrow_constructible_v<V, Args&&...>)
94 : Result{std::in_place, std::forward<Args>(args)...} {
95 }
96
97 // _error is copied, not moved: it's also the discriminant that controls the value lifetime
98 Result(const Result& other) noexcept(std::is_nothrow_copy_constructible_v<V>) : _error{other._error} {
99 if (!_error) {
100 ::new (&_value.value) V(other._value.value);
101 }
102 }
103
104 Result(Result&& other) noexcept(std::is_nothrow_move_constructible_v<V>) : _error{other._error} {
105 if (!_error) {
106 ::new (&_value.value) V(std::move(other._value.value));
107 }
108 }
109
110 Result& operator=(const Result& other) noexcept(std::is_nothrow_copy_constructible_v<V> &&
111 std::is_nothrow_copy_assignable_v<V>) {
112 if (this != &other) {
113 if (!other._error) {
114 if (!_error) {
115 _value.value = other._value.value;
116 } else {
117 ::new (&_value.value) V(other._value.value);
118 _error = nullptr;
119 }
120 } else {
121 if (!_error) {
122 _value.value.~V();
123 }
124 _error = other._error;
125 }
126 }
127 return *this;
128 }
129
130 Result& operator=(Result&& other) noexcept(std::is_nothrow_move_constructible_v<V> &&
131 std::is_nothrow_move_assignable_v<V>) {
132 if (this != &other) {
133 if (!other._error) {
134 if (!_error) {
135 _value.value = std::move(other._value.value);
136 } else {
137 ::new (&_value.value) V(std::move(other._value.value));
138 _error = nullptr;
139 }
140 } else {
141 if (!_error) {
142 _value.value.~V();
143 }
144 _error = other._error;
145 }
146 }
147 return *this;
148 }
149
150 template <typename Arg, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Arg>, Result>>>
152 *this = Result{std::forward<Arg>(arg)};
153 return *this;
154 }
155
157 if (!_error) {
158 _value.value.~V();
159 }
160 }
161
162 [[nodiscard]] explicit operator bool() const noexcept {
163 return !_error;
164 }
165
166 void Ok() & = delete;
167 void Ok() const&& = delete;
168 void Value() & = delete;
169 void Value() const&& = delete;
170 void Error() & = delete;
171 void Error() const&& = delete;
172
173 [[nodiscard]] V&& Ok() && {
174 if (_error) {
175 std::rethrow_exception(_error);
176 }
177 return std::move(_value.value);
178 }
179 [[nodiscard]] const V& Ok() const& {
180 if (_error) {
181 std::rethrow_exception(_error);
182 }
183 return _value.value;
184 }
185
186 [[nodiscard]] V&& Value() && noexcept {
187 YACLIB_ASSERT(!_error);
188 return std::move(_value.value);
189 }
190 [[nodiscard]] const V& Value() const& noexcept {
191 YACLIB_ASSERT(!_error);
192 return _value.value;
193 }
194
195 [[nodiscard]] const std::exception_ptr& Error() && noexcept {
196 YACLIB_ASSERT(_error);
197 return _error;
198 }
199 [[nodiscard]] const std::exception_ptr& Error() const& noexcept {
200 YACLIB_ASSERT(_error);
201 return _error;
202 }
203
204 private:
205 std::exception_ptr _error;
206 YACLIB_RESULT_EMPTY_VALUE State _value;
207};
208
209extern template class Result<>;
210
211/**
212 * Default result trait, describes how async abstractions create and inspect results
213 *
214 * A custom trait should provide the same interface, its container should be copyable iff V is copyable.
215 * MakeResult<V>(StopTag) and MakeResult<V>(std::exception_ptr) must not throw: they are invoked
216 * inside noexcept cancellation and teardown paths, a throw there will std::terminate
217 */
219 template <typename V>
221
222 using Error = std::exception_ptr;
223
224 template <typename R>
226
227 template <typename V, typename... Args>
229 static_assert(sizeof...(Args) > 0, "Use MakeResult<V>(Unit{}) to construct default value");
230 using Head = std::decay_t<head_t<Args&&...>>;
231 if constexpr (sizeof...(Args) == 1 && std::is_same_v<Head, Unit>) {
232 return yaclib::Result<V>{std::in_place};
233 } else if constexpr (std::is_same_v<Head, std::in_place_t> ||
234 (sizeof...(Args) == 1 &&
235 (std::is_same_v<Head, StopTag> || std::is_same_v<Head, std::exception_ptr> ||
236 std::is_same_v<Head, yaclib::Result<V>>))) {
237 return yaclib::Result<V>{std::forward<Args>(args)...};
238 } else {
239 return yaclib::Result<V>{std::in_place, std::forward<Args>(args)...};
240 }
241 }
242
243 template <typename V>
244 YACLIB_INLINE static bool Ok(const yaclib::Result<V>& r) noexcept {
245 return static_cast<bool>(r);
246 }
247
248 template <typename R>
249 YACLIB_INLINE static decltype(auto) GetValue(R&& r) noexcept {
250 return std::forward<R>(r).Value();
251 }
252
253 template <typename R>
254 YACLIB_INLINE static decltype(auto) GetError(R&& r) noexcept {
255 return std::forward<R>(r).Error();
256 }
257
258 template <typename R>
259 YACLIB_INLINE static decltype(auto) Get(R&& r) {
260 return std::forward<R>(r).Ok();
261 }
262
263 YACLIB_INLINE static bool IsStop(const std::exception_ptr& error) noexcept {
264 return yaclib::IsStop(error);
265 }
266};
267
268/**
269 * Trait used by default for all async abstractions, a separate struct so it can be forward declared
270 */
272
273} // namespace yaclib
Encapsulated return value from caller.
Definition result.hpp:52
void Ok() const &&=delete
const V & Ok() const &
Definition result.hpp:179
Result(StopTag) noexcept
Definition result.hpp:71
const std::exception_ptr & Error() const &noexcept
Definition result.hpp:199
~Result() noexcept
Definition result.hpp:156
Result(Result &&other) noexcept(std::is_nothrow_move_constructible_v< V >)
Definition result.hpp:104
Result(std::exception_ptr error) noexcept
Definition result.hpp:74
Result(const Result &other) noexcept(std::is_nothrow_copy_constructible_v< V >)
Definition result.hpp:98
Result & operator=(Result &&other) noexcept(std::is_nothrow_move_constructible_v< V > &&std::is_nothrow_move_assignable_v< V >)
Definition result.hpp:130
V && Value() &&noexcept
Definition result.hpp:186
Result(std::in_place_t, Args &&... args) noexcept(std::is_nothrow_constructible_v< V, Args &&... >)
Definition result.hpp:83
const V & Value() const &noexcept
Definition result.hpp:190
Result & operator=(Arg &&arg)
Definition result.hpp:151
Result & operator=(const Result &other) noexcept(std::is_nothrow_copy_constructible_v< V > &&std::is_nothrow_copy_assignable_v< V >)
Definition result.hpp:110
const std::exception_ptr & Error() &&noexcept
Definition result.hpp:195
Result() noexcept
Definition result.hpp:68
Result(Args &&... args) noexcept(std::is_nothrow_constructible_v< V, Args &&... >)
Definition result.hpp:93
void Ok() &=delete
#define YACLIB_ASSERT(cond)
Definition log.hpp:85
Contract< V, T > MakeContract()
Creates related future and promise.
Definition contract.hpp:25
typename detail::Head< Args... >::Type head_t
bool IsStop(const std::exception_ptr &error) noexcept
Check if error represents cancellation,.
Definition result.cpp:14
const std::exception_ptr & StopPtr() noexcept
Cached std::exception_ptr with StopException, copy is cheap and never allocates.
Definition result.cpp:7
#define YACLIB_RESULT_EMPTY_VALUE
Definition result.hpp:21
Trait used by default for all async abstractions, a separate struct so it can be forward declared.
Definition result.hpp:271
Default result trait, describes how async abstractions create and inspect results.
Definition result.hpp:218
static YACLIB_INLINE yaclib::Result< V > MakeResult(Args &&... args)
Definition result.hpp:228
static YACLIB_INLINE bool IsStop(const std::exception_ptr &error) noexcept
Definition result.hpp:263
typename detail::InstantiationType< yaclib::Result, R >::Value Value
Definition result.hpp:225
static YACLIB_INLINE decltype(auto) Get(R &&r)
Definition result.hpp:259
static YACLIB_INLINE decltype(auto) GetError(R &&r) noexcept
Definition result.hpp:254
static YACLIB_INLINE decltype(auto) GetValue(R &&r) noexcept
Definition result.hpp:249
std::exception_ptr Error
Definition result.hpp:222
static YACLIB_INLINE bool Ok(const yaclib::Result< V > &r) noexcept
Definition result.hpp:244
Exception that represents cancellation of an async operation,.
Definition result.hpp:27
const char * what() const noexcept override
Definition result.hpp:28