open62541pp 0.17.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_(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 constexpr Result() noexcept(std::is_nothrow_default_constructible_v<T>)
61 : code_(UA_STATUSCODE_GOOD),
62 maybeValue_({}) {}
63
64 // NOLINTBEGIN(hicpp-explicit-conversions)
65
66 /**
67 * Construct a Result from a value (lvalue) and a StatusCode.
68 */
69 constexpr Result(
70 const T& value, StatusCode code = UA_STATUSCODE_GOOD
71 ) noexcept(std::is_nothrow_copy_constructible_v<T>)
72 : code_(code),
73 maybeValue_(value) {}
74
75 /**
76 * Construct a Result from a value (rvalue) and a StatusCode.
77 */
78 constexpr Result(
79 T&& value, StatusCode code = UA_STATUSCODE_GOOD
80 ) noexcept(std::is_nothrow_move_constructible_v<T>)
81 : code_(code),
82 maybeValue_(std::move(value)) {}
83
84 /**
85 * Construct a Result from a BadResult.
86 */
87 constexpr Result(BadResult error) noexcept
88 : code_(error.code()),
89 maybeValue_(std::nullopt) {}
90
91 // NOLINTEND(hicpp-explicit-conversions)
92
93 // NOLINTBEGIN(bugprone-unchecked-optional-access)
94
95 /**
96 * Get the value of the Result.
97 * Accessing a Result without a value leads to undefined behavior.
98 */
99 constexpr T* operator->() noexcept {
100 return &(*maybeValue_);
101 }
102
103 /// @copydoc operator->
104 constexpr const T* operator->() const noexcept {
105 return &(*maybeValue_);
106 }
107
108 /**
109 * Get the value of the Result.
110 * Accessing a Result without a value leads to undefined behavior.
111 */
112 constexpr T& operator*() & noexcept {
113 return *maybeValue_;
114 }
115
116 /// @copydoc operator*
117 constexpr const T& operator*() const& noexcept {
118 return *maybeValue_;
119 }
120
121 /// @copydoc operator*
122 constexpr T&& operator*() && noexcept {
123 return std::move(*maybeValue_);
124 }
125
126 /// @copydoc operator*
127 constexpr const T&& operator*() const&& noexcept {
128 return std::move(*maybeValue_);
129 }
130
131 // NOLINTEND(bugprone-unchecked-optional-access)
132
133 /**
134 * Get the StatusCode of the Result.
135 */
136 constexpr StatusCode code() const noexcept {
137 return code_;
138 }
139
140 /**
141 * Check if the Result has a value.
142 */
143 constexpr explicit operator bool() const noexcept {
144 return hasValue();
145 }
146
147 /**
148 * Check if the Result has a value.
149 */
150 constexpr bool hasValue() const noexcept {
151 return maybeValue_.has_value();
152 }
153
154 /**
155 * Get the value of the Result.
156 * @exception BadStatus If the Result does not have a value (bad StatusCode).
157 */
158 constexpr T& value() & {
159 checkIsBad();
160 return **this;
161 }
162
163 /// @copydoc value
164 constexpr const T& value() const& {
165 checkIsBad();
166 return **this;
167 }
168
169 /// @copydoc value
170 constexpr T&& value() && {
171 checkIsBad();
172 return std::move(**this);
173 }
174
175 /// @copydoc value
176 constexpr const T&& value() const&& {
177 checkIsBad();
178 return std::move(**this);
179 }
180
181 /**
182 * Get the value of the Result or a default value.
183 * The default value is returned in case of an bad StatusCode.
184 */
185 template <typename U>
186 constexpr T valueOr(U&& defaultValue) const& {
187 return !isBad() ? **this : static_cast<T>(std::forward<U>(defaultValue));
188 }
189
190 /// @copydoc valueOr
191 template <typename U>
192 constexpr T valueOr(U&& defaultValue) && {
193 return !isBad() ? std::move(**this) : static_cast<T>(std::forward<U>(defaultValue));
194 }
195
196 /**
197 * Transforms `Result<T>` to `Result<U>` using the given value transformation function.
198 * The function is only applied if the Result has a value.
199 * Otherwise `Result<U>` with the same bad StatusCode is returned.
200 * @param func Callable with the signature `U(T&&)`
201 */
202 template <typename F>
203 constexpr auto transform(F&& func) & {
204 return transformImpl(*this, std::forward<F>(func));
205 }
206
207 /// @copydoc transform
208 template <typename F>
209 constexpr auto transform(F&& func) const& {
210 return transformImpl(*this, std::forward<F>(func));
211 }
212
213 /// @copydoc transform
214 template <typename F>
215 constexpr auto transform(F&& func) && {
216 return transformImpl(std::move(*this), std::forward<F>(func));
217 }
218
219 /// @copydoc transform
220 template <typename F>
221 constexpr auto transform(F&& func) const&& {
222 return transformImpl(std::move(*this), std::forward<F>(func));
223 }
224
225 /**
226 * Transforms `Result<T>` to `Result<U>` using the given function.
227 * The function is only applied if the Result has a value.
228 * Otherwise `Result<U>` with the same bad StatusCode is returned.
229 * @param func Callable with the signature `Result<U>(T&&)` or `Result<U>(T&&, StatusCode)`
230 */
231 template <typename F>
232 constexpr auto andThen(F&& func) & {
233 return andThenImpl(*this, std::forward<F>(func));
234 }
235
236 /// @copydoc andThen
237 template <typename F>
238 constexpr auto andThen(F&& func) const& {
239 return andThenImpl(*this, std::forward<F>(func));
240 }
241
242 /// @copydoc andThen
243 template <typename F>
244 constexpr auto andThen(F&& func) && {
245 return andThenImpl(std::move(*this), std::forward<F>(func));
246 }
247
248 /// @copydoc andThen
249 template <typename F>
250 constexpr auto andThen(F&& func) const&& {
251 return andThenImpl(std::move(*this), std::forward<F>(func));
252 }
253
254 /**
255 * Transforms `Result<T>` with a bad StatusCode to `Result<T>` using the given function.
256 * The function is only applied if the Result has **no** value.
257 * Otherwise the same `Result<T>` is returned.
258 * @param func Callable with the signature `Result<T>(StatusCode)`
259 */
260 template <typename F>
261 constexpr auto orElse(F&& func) & {
262 return orElseImpl(*this, std::forward<F>(func));
263 }
264
265 /// @copydoc orElse
266 template <typename F>
267 constexpr auto orElse(F&& func) const& {
268 return orElseImpl(*this, std::forward<F>(func));
269 }
270
271 /// @copydoc orElse
272 template <typename F>
273 constexpr auto orElse(F&& func) && {
274 return orElseImpl(std::move(*this), std::forward<F>(func));
275 }
276
277 /// @copydoc orElse
278 template <typename F>
279 constexpr auto orElse(F&& func) const&& {
280 return orElseImpl(std::move(*this), std::forward<F>(func));
281 }
282
283private:
284 template <typename Self, typename F>
285 static auto transformImpl(Self&& self, F&& func) {
286 using Value = decltype(*std::forward<Self>(self));
287 using NewValue = std::remove_cv_t<std::invoke_result_t<F, Value>>;
288 if (self.hasValue()) {
289 if constexpr (std::is_void_v<NewValue>) {
290 return Result<NewValue>();
291 } else {
292 return Result<NewValue>(
293 std::invoke(std::forward<F>(func), *std::forward<Self>(self)), self.code()
294 );
295 }
296 } else {
297 return Result<NewValue>(BadResult(self.code()));
298 }
299 }
300
301 template <typename Self, typename F>
302 static auto andThenImpl(Self&& self, F&& func) {
303 using Value = decltype(*std::forward<Self>(self));
304 if constexpr (std::is_invocable_v<F, Value, StatusCode>) {
305 using NewResult = std::remove_cv_t<std::invoke_result_t<F, Value, StatusCode>>;
306 return self.hasValue()
307 ? std::invoke(std::forward<F>(func), *std::forward<Self>(self), self.code())
308 : NewResult(BadResult(self.code()));
309 } else {
310 using NewResult = std::remove_cv_t<std::invoke_result_t<F, Value>>;
311 return self.hasValue()
312 ? std::invoke(std::forward<F>(func), *std::forward<Self>(self))
313 : NewResult(BadResult(self.code()));
314 }
315 }
316
317 template <typename Self, typename F>
318 static auto orElseImpl(Self&& self, F&& func) {
319 using NewResult = std::remove_cv_t<std::invoke_result_t<F, decltype(self.code())>>;
320 return self.hasValue()
321 ? std::forward<Self>(self)
322 : NewResult(std::invoke(std::forward<F>(func), self.code()));
323 }
324
325 constexpr bool isBad() const noexcept {
326 return code().isBad();
327 }
328
329 constexpr void checkIsBad() const {
330 code().throwIfBad();
331 }
332
333 StatusCode code_;
334 std::optional<T> maybeValue_;
335};
336
337/**
338 * Template specialization of Result class for `void` types.
339 * Result<void> contains only a StatusCode.
340 */
341template <>
342class [[nodiscard]] Result<void> {
343public:
344 /**
345 * Create a default Result (good StatusCode).
346 */
347 constexpr Result() noexcept
348 : code_(UA_STATUSCODE_GOOD) {}
349
350 /**
351 * Create a Result with the given StatusCode.
352 */
353 constexpr Result(StatusCode code) noexcept // NOLINT(hicpp-explicit-conversions)
354 : code_(code) {}
355
356 /**
357 * Create a Result with the given error.
358 */
359 constexpr Result(BadResult error) noexcept // NOLINT(hicpp-explicit-conversions)
360 : code_(error.code()) {}
361
362 constexpr void operator*() const noexcept {}
363
364 /**
365 * Get the code of the Result.
366 */
367 constexpr StatusCode code() const noexcept {
368 return code_;
369 }
370
371 /**
372 * Check if the Result has a non-bad StatusCode.
373 */
374 constexpr explicit operator bool() const noexcept {
375 return hasValue();
376 }
377
378 /*
379 * Check if the Result has a non-bad StatusCode.
380 */
381 constexpr bool hasValue() const noexcept {
382 return !code_.isBad();
383 }
384
385 /**
386 * Get the value of the Result.
387 * @exception BadStatus If the Result does have a bad StatusCode.
388 */
389 constexpr void value() const {
390 code().throwIfBad();
391 }
392
393private:
394 StatusCode code_;
395};
396
397} // 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:389
constexpr Result() noexcept
Create a default Result (good StatusCode).
Definition result.hpp:347
constexpr bool hasValue() const noexcept
Definition result.hpp:381
constexpr Result(BadResult error) noexcept
Create a Result with the given error.
Definition result.hpp:359
constexpr Result(StatusCode code) noexcept
Create a Result with the given StatusCode.
Definition result.hpp:353
constexpr StatusCode code() const noexcept
Get the code of the Result.
Definition result.hpp:367
constexpr void operator*() const noexcept
Definition result.hpp:362
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:279
constexpr auto andThen(F &&func) const &&
Transforms Result<T> to Result<U> using the given function.
Definition result.hpp:250
constexpr T valueOr(U &&defaultValue) &&
Get the value of the Result or a default value.
Definition result.hpp:192
constexpr T & operator*() &noexcept
Get the value of the Result.
Definition result.hpp:112
constexpr const T & value() const &
Get the value of the Result.
Definition result.hpp:164
constexpr T valueOr(U &&defaultValue) const &
Get the value of the Result or a default value.
Definition result.hpp:186
constexpr T && operator*() &&noexcept
Get the value of the Result.
Definition result.hpp:122
constexpr auto transform(F &&func) &
Transforms Result<T> to Result<U> using the given value transformation function.
Definition result.hpp:203
constexpr T && value() &&
Get the value of the Result.
Definition result.hpp:170
constexpr const T & operator*() const &noexcept
Get the value of the Result.
Definition result.hpp:117
constexpr auto orElse(F &&func) &&
Transforms Result<T> with a bad StatusCode to Result<T> using the given function.
Definition result.hpp:273
constexpr Result() noexcept(std::is_nothrow_default_constructible_v< T >)
Default constructor (default-initialized value and good StatusCode).
Definition result.hpp:60
constexpr bool hasValue() const noexcept
Check if the Result has a value.
Definition result.hpp:150
constexpr auto andThen(F &&func) &&
Transforms Result<T> to Result<U> using the given function.
Definition result.hpp:244
constexpr auto transform(F &&func) const &&
Transforms Result<T> to Result<U> using the given value transformation function.
Definition result.hpp:221
constexpr auto orElse(F &&func) const &
Transforms Result<T> with a bad StatusCode to Result<T> using the given function.
Definition result.hpp:267
constexpr const T && operator*() const &&noexcept
Get the value of the Result.
Definition result.hpp:127
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:69
constexpr const T && value() const &&
Get the value of the Result.
Definition result.hpp:176
constexpr auto transform(F &&func) &&
Transforms Result<T> to Result<U> using the given value transformation function.
Definition result.hpp:215
constexpr auto orElse(F &&func) &
Transforms Result<T> with a bad StatusCode to Result<T> using the given function.
Definition result.hpp:261
constexpr Result(BadResult error) noexcept
Construct a Result from a BadResult.
Definition result.hpp:87
constexpr auto transform(F &&func) const &
Transforms Result<T> to Result<U> using the given value transformation function.
Definition result.hpp:209
constexpr auto andThen(F &&func) const &
Transforms Result<T> to Result<U> using the given function.
Definition result.hpp:238
constexpr auto andThen(F &&func) &
Transforms Result<T> to Result<U> using the given function.
Definition result.hpp:232
constexpr T * operator->() noexcept
Get the value of the Result.
Definition result.hpp:99
constexpr T & value() &
Get the value of the Result.
Definition result.hpp:158
constexpr const T * operator->() const noexcept
Get the value of the Result.
Definition result.hpp:104
constexpr StatusCode code() const noexcept
Get the StatusCode of the Result.
Definition result.hpp:136
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:78
UA_StatusCode wrapper class.
Definition types.hpp:46
constexpr bool isBad() const noexcept
Check if the status code is bad.
Definition types.hpp:78
@ Value
The most recent value of the variable that the server has.