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