open62541pp 0.22.0
C++ wrapper of open62541
Loading...
Searching...
No Matches
result.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <cassert>
4#include <functional> // invoke
5#include <optional>
6#include <type_traits>
7#include <utility> // forward, move
8
10#include "open62541pp/types.hpp" // StatusCode
11
12namespace opcua {
13
14/**
15 * Represents a bad result stored in `Result`.
16 */
17class BadResult {
18public:
19 /**
20 * Construct a BadResult from a bad StatusCode.
21 */
22 constexpr explicit BadResult(StatusCode code) noexcept
23 : code_{std::move(code)} {
24 assert(code_.isBad());
25 }
26
27 /**
28 * Get the StatusCode.
29 */
30 constexpr StatusCode code() const noexcept {
31 return code_;
32 }
33
34private:
35 StatusCode code_;
36};
37
38/**
39 * The template class Result encapsulates a StatusCode and optionally a value.
40 *
41 * A result may have one of the following contents:
42 * - a value and a StatusCode (usually good or uncertain)
43 * - no value and a bad StatusCode
44 *
45 * Result<void> is a template specialization containing only a StatusCode.
46 *
47 * The design is inspired by:
48 * - [C++ 23's `std::expected` type](https://en.cppreference.com/w/cpp/utility/expected)
49 * - [Rust's `Result` type](https://doc.rust-lang.org/std/result)
50 * - [Swift's `Result` enumeration](https://developer.apple.com/documentation/swift/result)
51 */
52template <typename T>
53class [[nodiscard]] Result {
54public:
55 using ValueType = T;
56
57 /**
58 * Default constructor (default-initialized value and good StatusCode).
59 */
60 template <typename U = T, std::enable_if_t<std::is_default_constructible_v<U>, int> = 0>
61 constexpr Result() noexcept(std::is_nothrow_default_constructible_v<T>)
62 : code_{UA_STATUSCODE_GOOD},
63 maybeValue_{std::in_place} {}
64
65 // NOLINTBEGIN(*explicit-conversions)
66
67 /**
68 * Construct a Result from a value (lvalue) and a StatusCode.
69 */
70 constexpr Result(
71 const T& value, StatusCode code = UA_STATUSCODE_GOOD
72 ) noexcept(std::is_nothrow_copy_constructible_v<T>)
73 : code_{std::move(code)},
74 maybeValue_{value} {}
75
76 /**
77 * Construct a Result from a value (rvalue) and a StatusCode.
78 */
79 constexpr Result(
80 T&& value, StatusCode code = UA_STATUSCODE_GOOD
81 ) noexcept(std::is_nothrow_move_constructible_v<T>)
82 : code_{std::move(code)},
83 maybeValue_{std::move(value)} {}
84
85 /**
86 * Construct a Result from a BadResult.
87 */
88 constexpr Result(const BadResult& error) noexcept
89 : code_{error.code()},
90 maybeValue_{std::nullopt} {}
91
92 // NOLINTEND(*explicit-conversions)
93
94 // NOLINTBEGIN(bugprone-unchecked-optional-access)
95
96 /**
97 * Get the value of the Result.
98 * Accessing a Result without a value leads to undefined behavior.
99 */
100 constexpr T* operator->() noexcept {
101 return &(*maybeValue_);
102 }
103
104 /// @copydoc operator->
105 constexpr const T* operator->() const noexcept {
106 return &(*maybeValue_);
107 }
108
109 /**
110 * Get the value of the Result.
111 * Accessing a Result without a value leads to undefined behavior.
112 */
113 constexpr T& operator*() & noexcept {
114 return *maybeValue_;
115 }
116
117 /// @copydoc operator*
118 constexpr const T& operator*() const& noexcept {
119 return *maybeValue_;
120 }
121
122 /// @copydoc operator*
123 constexpr T&& operator*() && noexcept {
124 return std::move(*maybeValue_);
125 }
126
127 /// @copydoc operator*
128 constexpr const T&& operator*() const&& noexcept {
129 return std::move(*maybeValue_);
130 }
131
132 // NOLINTEND(bugprone-unchecked-optional-access)
133
134 /**
135 * Get the StatusCode of the Result.
136 */
137 constexpr StatusCode code() const noexcept {
138 return code_;
139 }
140
141 /**
142 * Check if the Result has a value.
143 */
144 constexpr explicit operator bool() const noexcept {
145 return hasValue();
146 }
147
148 /**
149 * Check if the Result has a value.
150 */
151 constexpr bool hasValue() const noexcept {
152 return maybeValue_.has_value();
153 }
154
155 /**
156 * Get the value of the Result.
157 * @exception BadStatus If the Result does not have a value (bad StatusCode).
158 */
159 constexpr T& value() & {
160 checkIsBad();
161 return **this;
162 }
163
164 /// @copydoc value
165 constexpr const T& value() const& {
166 checkIsBad();
167 return **this;
168 }
169
170 /// @copydoc value
171 constexpr T&& value() && {
172 checkIsBad();
173 return std::move(**this);
174 }
175
176 /// @copydoc value
177 constexpr const T&& value() const&& {
178 checkIsBad();
179 return std::move(**this);
180 }
181
182 /**
183 * Get the value of the Result or a default value.
184 * The default value is returned in case of an bad StatusCode.
185 */
186 template <typename U>
187 constexpr T valueOr(U&& defaultValue) const& {
188 return !isBad() ? **this : static_cast<T>(std::forward<U>(defaultValue));
189 }
190
191 /// @copydoc valueOr
192 template <typename U>
193 constexpr T valueOr(U&& defaultValue) && {
194 return !isBad() ? std::move(**this) : static_cast<T>(std::forward<U>(defaultValue));
195 }
196
197 /**
198 * Transforms `Result<T>` to `Result<U>` using the given value transformation function.
199 * The function is only applied if the Result has a value.
200 * Otherwise `Result<U>` with the same bad StatusCode is returned.
201 * @param func Callable with the signature `U(T&&)`
202 */
203 template <typename F>
204 constexpr auto transform(F&& func) & {
205 return transformImpl(*this, std::forward<F>(func));
206 }
207
208 /// @copydoc transform
209 template <typename F>
210 constexpr auto transform(F&& func) const& {
211 return transformImpl(*this, std::forward<F>(func));
212 }
213
214 /// @copydoc transform
215 template <typename F>
216 constexpr auto transform(F&& func) && {
217 return transformImpl(std::move(*this), std::forward<F>(func));
218 }
219
220 /// @copydoc transform
221 template <typename F>
222 constexpr auto transform(F&& func) const&& {
223 return transformImpl(std::move(*this), std::forward<F>(func));
224 }
225
226 /**
227 * Transforms `Result<T>` to `Result<U>` using the given function.
228 * The function is only applied if the Result has a value.
229 * Otherwise `Result<U>` with the same bad StatusCode is returned.
230 * @param func Callable with the signature `Result<U>(T&&)` or `Result<U>(T&&, StatusCode)`
231 */
232 template <typename F>
233 constexpr auto andThen(F&& func) & {
234 return andThenImpl(*this, std::forward<F>(func));
235 }
236
237 /// @copydoc andThen
238 template <typename F>
239 constexpr auto andThen(F&& func) const& {
240 return andThenImpl(*this, std::forward<F>(func));
241 }
242
243 /// @copydoc andThen
244 template <typename F>
245 constexpr auto andThen(F&& func) && {
246 return andThenImpl(std::move(*this), std::forward<F>(func));
247 }
248
249 /// @copydoc andThen
250 template <typename F>
251 constexpr auto andThen(F&& func) const&& {
252 return andThenImpl(std::move(*this), std::forward<F>(func));
253 }
254
255 /**
256 * Transforms `Result<T>` with a bad StatusCode to `Result<T>` using the given function.
257 * The function is only applied if the Result has **no** value.
258 * Otherwise the same `Result<T>` is returned.
259 * @param func Callable with the signature `Result<T>(StatusCode)`
260 */
261 template <typename F>
262 constexpr auto orElse(F&& func) & {
263 return orElseImpl(*this, std::forward<F>(func));
264 }
265
266 /// @copydoc orElse
267 template <typename F>
268 constexpr auto orElse(F&& func) const& {
269 return orElseImpl(*this, std::forward<F>(func));
270 }
271
272 /// @copydoc orElse
273 template <typename F>
274 constexpr auto orElse(F&& func) && {
275 return orElseImpl(std::move(*this), std::forward<F>(func));
276 }
277
278 /// @copydoc orElse
279 template <typename F>
280 constexpr auto orElse(F&& func) const&& {
281 return orElseImpl(std::move(*this), std::forward<F>(func));
282 }
283
284private:
285 template <typename Self, typename F>
286 static auto transformImpl(Self&& self, F&& func) {
287 using Value = decltype(*std::forward<Self>(self));
288 using NewValue = std::remove_cv_t<std::invoke_result_t<F, Value>>;
289 if (self.hasValue()) {
290 if constexpr (std::is_void_v<NewValue>) {
291 return Result<NewValue>();
292 } else {
293 return Result<NewValue>(
294 std::invoke(std::forward<F>(func), *std::forward<Self>(self)), self.code()
295 );
296 }
297 } else {
298 return Result<NewValue>(BadResult(self.code()));
299 }
300 }
301
302 template <typename Self, typename F>
303 static auto andThenImpl(Self&& self, F&& func) {
304 using Value = decltype(*std::forward<Self>(self));
305 if constexpr (std::is_invocable_v<F, Value, StatusCode>) {
306 using NewResult = std::remove_cv_t<std::invoke_result_t<F, Value, StatusCode>>;
307 return self.hasValue()
308 ? std::invoke(std::forward<F>(func), *std::forward<Self>(self), self.code())
309 : NewResult(BadResult(self.code()));
310 } else {
311 using NewResult = std::remove_cv_t<std::invoke_result_t<F, Value>>;
312 return self.hasValue()
313 ? std::invoke(std::forward<F>(func), *std::forward<Self>(self))
314 : NewResult(BadResult(self.code()));
315 }
316 }
317
318 template <typename Self, typename F>
319 static auto orElseImpl(Self&& self, F&& func) {
320 using NewResult = std::remove_cv_t<std::invoke_result_t<F, decltype(self.code())>>;
321 return self.hasValue()
322 ? std::forward<Self>(self)
323 : NewResult(std::invoke(std::forward<F>(func), self.code()));
324 }
325
326 constexpr bool isBad() const noexcept {
327 return code().isBad();
328 }
329
330 constexpr void checkIsBad() const {
331 code().throwIfBad();
332 }
333
334 StatusCode code_;
335 std::optional<T> maybeValue_;
336};
337
338/**
339 * Template specialization of Result class for `void` types.
340 * Result<void> contains only a StatusCode.
341 */
342template <>
343class [[nodiscard]] Result<void> {
344public:
345 /**
346 * Create a default Result (good StatusCode).
347 */
348 constexpr Result() noexcept
349 : code_{UA_STATUSCODE_GOOD} {}
350
351 /**
352 * Create a Result with the given StatusCode.
353 */
354 constexpr Result(StatusCode code) noexcept // NOLINT(*explicit-conversions)
355 : code_{std::move(code)} {}
356
357 /**
358 * Create a Result with the given error.
359 */
360 constexpr Result(const BadResult& error) noexcept // NOLINT(*explicit-conversions)
361 : code_{error.code()} {}
362
363 constexpr void operator*() const noexcept {}
364
365 /**
366 * Get the code of the Result.
367 */
368 constexpr StatusCode code() const noexcept {
369 return code_;
370 }
371
372 /**
373 * Check if the Result has a non-bad StatusCode.
374 */
375 constexpr explicit operator bool() const noexcept {
376 return hasValue();
377 }
378
379 /*
380 * Check if the Result has a non-bad StatusCode.
381 */
382 constexpr bool hasValue() const noexcept {
383 return !code_.isBad();
384 }
385
386 /**
387 * Get the value of the Result.
388 * @exception BadStatus If the Result does have a bad StatusCode.
389 */
390 constexpr void value() const {
391 code().throwIfBad();
392 }
393
394private:
395 StatusCode code_;
396};
397
398} // namespace opcua
Represents a bad result stored in Result.
Definition result.hpp:17
constexpr StatusCode code() const noexcept
Get the StatusCode.
Definition result.hpp:30
constexpr BadResult(StatusCode code) noexcept
Construct a BadResult from a bad StatusCode.
Definition result.hpp:22
constexpr void value() const
Get the value of the Result.
Definition result.hpp:390
constexpr Result() noexcept
Create a default Result (good StatusCode).
Definition result.hpp:348
constexpr bool hasValue() const noexcept
Definition result.hpp:382
constexpr Result(StatusCode code) noexcept
Create a Result with the given StatusCode.
Definition result.hpp:354
constexpr Result(const BadResult &error) noexcept
Create a Result with the given error.
Definition result.hpp:360
constexpr StatusCode code() const noexcept
Get the code of the Result.
Definition result.hpp:368
constexpr void operator*() const noexcept
Definition result.hpp:363
The template class Result encapsulates a StatusCode and optionally a value.
Definition result.hpp:53
constexpr auto orElse(F &&func) const &&
Transforms Result<T> with a bad StatusCode to Result<T> using the given function.
Definition result.hpp:280
constexpr auto andThen(F &&func) const &&
Transforms Result<T> to Result<U> using the given function.
Definition result.hpp:251
constexpr T valueOr(U &&defaultValue) &&
Get the value of the Result or a default value.
Definition result.hpp:193
constexpr T & operator*() &noexcept
Get the value of the Result.
Definition result.hpp:113
constexpr const T & value() const &
Get the value of the Result.
Definition result.hpp:165
constexpr T valueOr(U &&defaultValue) const &
Get the value of the Result or a default value.
Definition result.hpp:187
constexpr T && operator*() &&noexcept
Get the value of the Result.
Definition result.hpp:123
constexpr auto transform(F &&func) &
Transforms Result<T> to Result<U> using the given value transformation function.
Definition result.hpp:204
constexpr T && value() &&
Get the value of the Result.
Definition result.hpp:171
constexpr const T & operator*() const &noexcept
Get the value of the Result.
Definition result.hpp:118
constexpr auto orElse(F &&func) &&
Transforms Result<T> with a bad StatusCode to Result<T> using the given function.
Definition result.hpp:274
constexpr bool hasValue() const noexcept
Check if the Result has a value.
Definition result.hpp:151
constexpr auto andThen(F &&func) &&
Transforms Result<T> to Result<U> using the given function.
Definition result.hpp:245
constexpr auto transform(F &&func) const &&
Transforms Result<T> to Result<U> using the given value transformation function.
Definition result.hpp:222
constexpr Result(const BadResult &error) noexcept
Construct a Result from a BadResult.
Definition result.hpp:88
constexpr auto orElse(F &&func) const &
Transforms Result<T> with a bad StatusCode to Result<T> using the given function.
Definition result.hpp:268
constexpr const T && operator*() const &&noexcept
Get the value of the Result.
Definition result.hpp:128
constexpr Result(const T &value, StatusCode code=UA_STATUSCODE_GOOD) noexcept(std::is_nothrow_copy_constructible_v< T >)
Construct a Result from a value (lvalue) and a StatusCode.
Definition result.hpp:70
constexpr const T && value() const &&
Get the value of the Result.
Definition result.hpp:177
constexpr auto transform(F &&func) &&
Transforms Result<T> to Result<U> using the given value transformation function.
Definition result.hpp:216
constexpr auto orElse(F &&func) &
Transforms Result<T> with a bad StatusCode to Result<T> using the given function.
Definition result.hpp:262
constexpr Result() noexcept(std::is_nothrow_default_constructible_v< T >)
Default constructor (default-initialized value and good StatusCode).
Definition result.hpp:61
constexpr auto transform(F &&func) const &
Transforms Result<T> to Result<U> using the given value transformation function.
Definition result.hpp:210
constexpr auto andThen(F &&func) const &
Transforms Result<T> to Result<U> using the given function.
Definition result.hpp:239
constexpr auto andThen(F &&func) &
Transforms Result<T> to Result<U> using the given function.
Definition result.hpp:233
constexpr T * operator->() noexcept
Get the value of the Result.
Definition result.hpp:100
constexpr T & value() &
Get the value of the Result.
Definition result.hpp:159
constexpr const T * operator->() const noexcept
Get the value of the Result.
Definition result.hpp:105
constexpr StatusCode code() const noexcept
Get the StatusCode of the Result.
Definition result.hpp:137
constexpr Result(T &&value, StatusCode code=UA_STATUSCODE_GOOD) noexcept(std::is_nothrow_move_constructible_v< T >)
Construct a Result from a value (rvalue) and a StatusCode.
Definition result.hpp:79
UA_StatusCode wrapper class.
Definition types.hpp:42
constexpr bool isBad() const noexcept
Check if the status code is bad.
Definition types.hpp:74
#define UA_STATUSCODE_GOOD