Sen API
Sen Libraries
Loading...
Searching...
No Matches
result.h
Go to the documentation of this file.
1// === result.h ========================================================================================================
2// Sen Infrastructure
3// Released under the Apache License v2.0 (SPDX-License-Identifier Apache-2.0).
4// See the LICENSE.txt file for more information.
5// © Airbus SAS, Airbus Helicopters, and Airbus Defence and Space SAU/GmbH/SAS.
6// =====================================================================================================================
7
8#ifndef SEN_CORE_BASE_RESULT_H
9#define SEN_CORE_BASE_RESULT_H
10
11// sen
12#include "sen/core/base/class_helpers.h" // NOLINT(misc-include-cleaner)
13
14// std
15#include <string_view>
16#include <tuple>
17#include <type_traits>
18#include <variant>
19
20namespace sen
21{
22
25
26namespace impl
27{
28
30template <typename T>
31struct Ok final
32{
33 using ValueT = T;
34
35 explicit Ok(T theVal) noexcept(std::is_nothrow_move_constructible_v<T>): val(std::move(theVal)) {}
36
37 ~Ok() noexcept = default;
38 SEN_COPY_CONSTRUCT(Ok) = default; // NOLINT(misc-include-cleaner)
39 SEN_MOVE_CONSTRUCT(Ok) = default; // NOLINT(misc-include-cleaner)
40 SEN_COPY_ASSIGN(Ok) = default; // NOLINT(misc-include-cleaner)
41 SEN_MOVE_ASSIGN(Ok) = default; // NOLINT(misc-include-cleaner)
42
43 T val; // NOLINT(misc-non-private-member-variables-in-classes)
44};
45
47template <>
48struct Ok<void> final
49{
50};
51
53template <typename E>
54struct Err final
55{
56 using ValueT = E;
57
58 explicit Err(E theVal) noexcept(std::is_nothrow_move_constructible_v<E>): val(std::move(theVal)) {}
59
60 ~Err() noexcept = default;
61 SEN_COPY_CONSTRUCT(Err) = default;
62 SEN_MOVE_CONSTRUCT(Err) = default;
63 SEN_COPY_ASSIGN(Err) = default;
64 SEN_MOVE_ASSIGN(Err) = default;
65
66 E val; // NOLINT(misc-non-private-member-variables-in-classes)
67};
68
70template <>
71struct Err<void> final
72{
73};
74
76template <typename U>
77using NonVoidT = std::enable_if_t<!std::is_void_v<U>, U>;
78
80void resultExpect(bool value, std::string_view errorMsg = {}) noexcept;
81
82} // namespace impl
83
133template <typename T, typename E>
134class [[nodiscard]] Result final
135{
136 static_assert(!::std::is_void_v<E>, "void error type is not allowed");
137
138public: // types
139 using ValueType = T;
140 using ErrorType = E;
141
142public: // construction
144 // NOLINTNEXTLINE(hicpp-explicit-conversions)
145 Result(impl::Ok<T>&& ok) noexcept: value_(std::in_place_type<T>, std::move(ok.val)) {}
146
148 // NOLINTNEXTLINE(hicpp-explicit-conversions)
149 Result(impl::Err<E>&& err) noexcept: value_(std::in_place_type<E>, std::move(err.val)) {}
150
151public: // special members
152 SEN_MOVE_CONSTRUCT(Result) = default;
153 SEN_COPY_CONSTRUCT(Result) = default;
154 SEN_MOVE_ASSIGN(Result) = default;
155 SEN_COPY_ASSIGN(Result) = default;
156 ~Result() = default;
157
158public: // conversion support
160 template <typename U,
161 typename G,
162 std::enable_if_t<std::conjunction_v<std::is_constructible<T, const U&>, std::is_constructible<E, const G&>>,
163 bool> = true>
164 // NOLINTNEXTLINE(hicpp-explicit-conversions)
165 Result(const Result<U, G>& other)
166 : value_(
167 [](const Result<U, G>& other)
168 {
169 if (other.isOk())
170 {
171 return decltype(value_)(std::in_place_type<T>, other.getValue());
172 }
173
174 return decltype(value_)(std::in_place_type<E>, other.getError());
175 }(other))
176 {
177 }
178
180 template <typename U,
181 typename G,
182 std::enable_if_t<std::conjunction_v<std::is_constructible<T, U>, std::is_constructible<E, G>>, bool> = true>
183 // NOLINTNEXTLINE(hicpp-explicit-conversions)
185 : value_(
186 [](Result<U, G>&& other)
187 {
188 if (other.isOk())
189 {
190 return decltype(value_)(std::in_place_type<T>, std::move(other).getValue());
191 }
192
193 return decltype(value_)(std::in_place_type<E>, std::move(other).getError());
194 }(std::move(other)))
195 {
196 }
197
198public: // comparison
199 bool operator==(const Result& other) const { return value_ == other.value_; }
200 bool operator!=(const Result& other) const { return !(*this == other); }
201
202public: // access
207 [[nodiscard]] bool isOk() const noexcept { return std::holds_alternative<T>(value_); }
208
213 [[nodiscard]] bool isError() const noexcept { return std::holds_alternative<E>(value_); }
214
219 explicit operator bool() const noexcept { return isOk(); }
220
226 template <typename U = T>
227 [[nodiscard]] const impl::NonVoidT<U>& getValueOr(const U& defaultVal) const noexcept;
228
236 template <typename U = T>
237 [[nodiscard]] const impl::NonVoidT<U>& getValue() const&
238 {
239 impl::resultExpect(isOk());
240 return std::get<T>(value_);
241 }
242
250 template <typename U = T>
251 [[nodiscard]] impl::NonVoidT<U>&& getValue() &&
252 {
253 impl::resultExpect(isOk());
254 return std::get<T>(std::move(value_));
255 }
256
264 [[nodiscard]] const E& getError() const
265 {
266 impl::resultExpect(isError());
267 return std::get<E>(value_);
268 }
269
272 template <typename U = T>
273 // resultExpect terminates the program when the state is wrong, so the std::get
274 // below cannot actually throw.
275 // NOLINTNEXTLINE(bugprone-exception-escape)
276 [[nodiscard]] const impl::NonVoidT<U>& expect(std::string_view errorMsg = {}) const noexcept
277 {
278 impl::resultExpect(isOk(), errorMsg);
279 return std::get<T>(value_);
280 }
281
282private:
283 std::variant<E, T> value_;
284};
285
288
289//--------------------------------------------------------------------------------------------------------------
290// Helpers
291//--------------------------------------------------------------------------------------------------------------
292
294template <typename T, typename CleanT = std::decay_t<T>>
295impl::Ok<CleanT> Ok(T&& val) noexcept; // NOLINT(readability-identifier-naming)
296
298template <typename E, typename CleanE = std::decay_t<E>>
299impl::Err<CleanE> Err(E&& val) noexcept; // NOLINT(readability-identifier-naming)
300
302impl::Ok<void> Ok() noexcept; // NOLINT(readability-identifier-naming)
303
305impl::Err<void> Err() noexcept; // NOLINT(readability-identifier-naming)
306
308
309//----------------------------------------------------------------------------------------------------------------------
310// Inline implementation
311//----------------------------------------------------------------------------------------------------------------------
312
313template <typename T, typename E>
314template <typename U>
315inline const impl::NonVoidT<U>& Result<T, E>::getValueOr(const U& defaultVal) const noexcept
316{
317 if (const auto* maybeVal = std::get_if<T>(&value_))
318 {
319 return *maybeVal;
320 }
321
322 return defaultVal;
323}
324
326template <typename E>
327class Result<void, E> final
328{
329 static_assert(!::std::is_void_v<E>, "void error type is not allowed");
330
331public:
332 using ValueType = void;
333 using ErrorType = E;
334
335public:
336 Result(impl::Ok<void>&& arg) noexcept: ok_(true) // NOLINT(hicpp-explicit-conversions)
337 {
338 std::ignore = arg;
339 }
340
341 template <typename T>
342 Result(Result<T, E>&& other) noexcept // NOLINT(hicpp-explicit-conversions)
343 : ok_(other.isOk())
344 {
345 }
346
347 Result(impl::Err<E>&& err) noexcept // NOLINT(hicpp-explicit-conversions)
348 : ok_(false), error_(std::move(err.val))
349 {
350 }
351
352public: // defaults
353 Result(Result&& other) noexcept = default;
354 Result(const Result& other) noexcept = default;
355 ~Result() noexcept = default;
356 Result& operator=(const Result& other) noexcept = default;
357 Result& operator=(Result&& other) noexcept = default;
358
359public: // comparison
360 bool operator==(const Result& other) const noexcept { return ok_ == other.ok_ && error_ == other.error_; }
361
362 bool operator!=(const Result& other) const noexcept { return !(*this == other); }
363
364public: // access
365 [[nodiscard]] bool isOk() const noexcept { return ok_; }
366
367 [[nodiscard]] bool isError() const noexcept { return !isOk(); }
368
369 explicit operator bool() const noexcept { return isOk(); }
370
371 [[nodiscard]] const E& getError() const noexcept
372 {
373 impl::resultExpect(isError());
374
375 return error_;
376 }
377
378private:
379 bool ok_;
380 E error_ = {};
381};
382
384template <>
385class Result<void, std::monostate> final
386{
387public:
388 using ValueType = void;
389 using ErrorType = std::monostate;
390
391public:
392 Result(impl::Ok<void>&& arg) noexcept: ok_(true) // NOLINT(hicpp-explicit-conversions)
393 {
394 std::ignore = arg;
395 }
396
397 Result(impl::Err<void>&& arg) noexcept: ok_(false) // NOLINT(hicpp-explicit-conversions)
398 {
399 std::ignore = arg;
400 }
401
402public: // defaults
403 Result(Result&& other) noexcept = default;
404 Result(const Result& other) noexcept = default;
405 ~Result() noexcept = default;
406 Result& operator=(const Result& other) noexcept = default;
407 Result& operator=(Result&& other) noexcept = default;
408
409public: // comparison
410 bool operator==(const Result& other) const noexcept { return ok_ == other.ok_; }
411
412 bool operator!=(const Result& other) const noexcept { return !(*this == other); }
413
414public: // access
415 [[nodiscard]] bool isOk() const noexcept { return ok_; }
416
417 [[nodiscard]] bool isError() const noexcept { return !isOk(); }
418
419 explicit operator bool() const noexcept { return isOk(); }
420
421private:
422 bool ok_;
423};
424
425template <typename T, typename CleanT>
426inline impl::Ok<CleanT> Ok(T&& val) noexcept // NOLINT(readability-identifier-naming)
427{
428 return impl::Ok<CleanT>(std::forward<T>(val));
429}
430
431template <typename E, typename CleanE>
432inline impl::Err<CleanE> Err(E&& val) noexcept // NOLINT(readability-identifier-naming)
433{
434 return impl::Err<CleanE>(std::forward<E>(val));
435}
436
437[[nodiscard]] inline impl::Ok<void> Ok() noexcept // NOLINT(readability-identifier-naming)
438{
439 return {};
440}
441
442[[nodiscard]] inline impl::Err<void> Err() noexcept // NOLINT(readability-identifier-naming)
443{
444 return {};
445}
446
447} // namespace sen
448
449#endif // SEN_CORE_BASE_RESULT_H
Here we define a set of template meta-programming helpers to let the compiler take some decisions bas...
Result(impl::Ok< void > &&arg) noexcept
Definition result.h:336
Result(Result< T, E > &&other) noexcept
Definition result.h:342
void ValueType
Definition result.h:332
const E & getError() const noexcept
Definition result.h:371
Result(Result &&other) noexcept=default
bool operator!=(const Result &other) const noexcept
Definition result.h:362
Result(const Result &other) noexcept=default
E ErrorType
Definition result.h:333
~Result() noexcept=default
bool isError() const noexcept
Definition result.h:367
Result(impl::Err< E > &&err) noexcept
Definition result.h:347
bool isOk() const noexcept
Definition result.h:365
Result(const Result &other) noexcept=default
bool isError() const noexcept
Definition result.h:417
std::monostate ErrorType
Definition result.h:389
bool isOk() const noexcept
Definition result.h:415
Result(impl::Err< void > &&arg) noexcept
Definition result.h:397
bool operator!=(const Result &other) const noexcept
Definition result.h:412
Result(Result &&other) noexcept=default
void ValueType
Definition result.h:388
Result(impl::Ok< void > &&arg) noexcept
Definition result.h:392
Result<T, E> is a template type that can be used to return and propagate errors. The intent is to rep...
Definition result.h:135
bool operator!=(const Result &other) const
Definition result.h:200
const impl::NonVoidT< U > & expect(std::string_view errorMsg={}) const noexcept
Extracts the value of the correct result, or terminates the program with a given error message.
Definition result.h:276
bool operator==(const Result &other) const
Definition result.h:199
bool isOk() const noexcept
Used to determine if the result is not an error.
Definition result.h:207
Result(Result< U, G > &&other)
Do a conversion construction from a compatible Result.
Definition result.h:184
~Result()=default
const impl::NonVoidT< U > & getValueOr(const U &defaultVal) const noexcept
Used to determine the return value. If the result indicates an error this method will return defaultV...
Definition result.h:315
impl::NonVoidT< U > && getValue() &&
Used to move out the success value, given that there is no error.
Definition result.h:251
const impl::NonVoidT< U > & getValue() const &
Used to determine the success value, given that there is no error.
Definition result.h:237
T ValueType
Definition result.h:139
Result(impl::Ok< T > &&ok) noexcept
Construct a Result that indicates success and that carries a valid return value.
Definition result.h:145
Result(impl::Err< E > &&err) noexcept
Construct a Result that indicates failure and that carries an error value.
Definition result.h:149
E ErrorType
Definition result.h:140
Result(const Result< U, G > &other)
Do a conversion construction from a compatible Result.
Definition result.h:165
const E & getError() const
Used to determine the error value, given that there is an error.
Definition result.h:264
bool isError() const noexcept
Used to determine if the result is an error.
Definition result.h:213
impl::Err< CleanE > Err(E &&val) noexcept
Helper (syntactic sugar) to create Results that indicate error.
Definition result.h:432
impl::Err< void > Err() noexcept
If E is void, use the void specialization of Err.
Definition result.h:442
impl::Ok< void > Ok() noexcept
If T is void, use the void specialization of Ok.
Definition result.h:437
Result< void, std::monostate > BoolResult
True or false result.
Definition result.h:287
Definition assert.h:17
STL namespace.