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 [[nodiscard]] const impl::NonVoidT<U>& expect(std::string_view errorMsg = {}) const noexcept
274 {
275 std::ignore = errorMsg; // <-- inspect this variable to check the error
276 return getValue();
277 }
278
279private:
280 std::variant<E, T> value_;
281};
282
285
286//--------------------------------------------------------------------------------------------------------------
287// Helpers
288//--------------------------------------------------------------------------------------------------------------
289
291template <typename T, typename CleanT = std::decay_t<T>>
292impl::Ok<CleanT> Ok(T&& val) noexcept; // NOLINT(readability-identifier-naming)
293
295template <typename E, typename CleanE = std::decay_t<E>>
296impl::Err<CleanE> Err(E&& val) noexcept; // NOLINT(readability-identifier-naming)
297
299impl::Ok<void> Ok() noexcept; // NOLINT(readability-identifier-naming)
300
302impl::Err<void> Err() noexcept; // NOLINT(readability-identifier-naming)
303
305
306//----------------------------------------------------------------------------------------------------------------------
307// Inline implementation
308//----------------------------------------------------------------------------------------------------------------------
309
310template <typename T, typename E>
311template <typename U>
312inline const impl::NonVoidT<U>& Result<T, E>::getValueOr(const U& defaultVal) const noexcept
313{
314 if (const auto* maybeVal = std::get_if<T>(&value_))
315 {
316 return *maybeVal;
317 }
318
319 return defaultVal;
320}
321
323template <typename E>
324class Result<void, E> final
325{
326 static_assert(!::std::is_void_v<E>, "void error type is not allowed");
327
328public:
329 using ValueType = void;
330 using ErrorType = E;
331
332public:
333 Result(impl::Ok<void>&& arg) noexcept: ok_(true) // NOLINT(hicpp-explicit-conversions)
334 {
335 std::ignore = arg;
336 }
337
338 template <typename T>
339 Result(Result<T, E>&& other) noexcept // NOLINT(hicpp-explicit-conversions)
340 : ok_(other.isOk())
341 {
342 }
343
344 Result(impl::Err<E>&& err) noexcept // NOLINT(hicpp-explicit-conversions)
345 : ok_(false), error_(std::move(err.val))
346 {
347 }
348
349public: // defaults
350 Result(Result&& other) noexcept = default;
351 Result(const Result& other) noexcept = default;
352 ~Result() noexcept = default;
353 Result& operator=(const Result& other) noexcept = default;
354 Result& operator=(Result&& other) noexcept = default;
355
356public: // comparison
357 bool operator==(const Result& other) const noexcept { return ok_ == other.ok_ && error_ == other.error_; }
358
359 bool operator!=(const Result& other) const noexcept { return !(*this == other); }
360
361public: // access
362 [[nodiscard]] bool isOk() const noexcept { return ok_; }
363
364 [[nodiscard]] bool isError() const noexcept { return !isOk(); }
365
366 explicit operator bool() const noexcept { return isOk(); }
367
368 [[nodiscard]] const E& getError() const noexcept
369 {
370 impl::resultExpect(isError());
371
372 return error_;
373 }
374
375private:
376 bool ok_;
377 E error_ = {};
378};
379
381template <>
382class Result<void, std::monostate> final
383{
384public:
385 using ValueType = void;
386 using ErrorType = std::monostate;
387
388public:
389 Result(impl::Ok<void>&& arg) noexcept: ok_(true) // NOLINT(hicpp-explicit-conversions)
390 {
391 std::ignore = arg;
392 }
393
394 Result(impl::Err<void>&& arg) noexcept: ok_(false) // NOLINT(hicpp-explicit-conversions)
395 {
396 std::ignore = arg;
397 }
398
399public: // defaults
400 Result(Result&& other) noexcept = default;
401 Result(const Result& other) noexcept = default;
402 ~Result() noexcept = default;
403 Result& operator=(const Result& other) noexcept = default;
404 Result& operator=(Result&& other) noexcept = default;
405
406public: // comparison
407 bool operator==(const Result& other) const noexcept { return ok_ == other.ok_; }
408
409 bool operator!=(const Result& other) const noexcept { return !(*this == other); }
410
411public: // access
412 [[nodiscard]] bool isOk() const noexcept { return ok_; }
413
414 [[nodiscard]] bool isError() const noexcept { return !isOk(); }
415
416 explicit operator bool() const noexcept { return isOk(); }
417
418private:
419 bool ok_;
420};
421
422template <typename T, typename CleanT>
423inline impl::Ok<CleanT> Ok(T&& val) noexcept // NOLINT(readability-identifier-naming)
424{
425 return impl::Ok<CleanT>(std::forward<T>(val));
426}
427
428template <typename E, typename CleanE>
429inline impl::Err<CleanE> Err(E&& val) noexcept // NOLINT(readability-identifier-naming)
430{
431 return impl::Err<CleanE>(std::forward<E>(val));
432}
433
434[[nodiscard]] inline impl::Ok<void> Ok() noexcept // NOLINT(readability-identifier-naming)
435{
436 return {};
437}
438
439[[nodiscard]] inline impl::Err<void> Err() noexcept // NOLINT(readability-identifier-naming)
440{
441 return {};
442}
443
444} // namespace sen
445
446#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:333
Result(Result< T, E > &&other) noexcept
Definition result.h:339
void ValueType
Definition result.h:329
const E & getError() const noexcept
Definition result.h:368
Result(Result &&other) noexcept=default
bool operator!=(const Result &other) const noexcept
Definition result.h:359
Result(const Result &other) noexcept=default
E ErrorType
Definition result.h:330
~Result() noexcept=default
bool isError() const noexcept
Definition result.h:364
Result(impl::Err< E > &&err) noexcept
Definition result.h:344
bool isOk() const noexcept
Definition result.h:362
Result(const Result &other) noexcept=default
bool isError() const noexcept
Definition result.h:414
std::monostate ErrorType
Definition result.h:386
bool isOk() const noexcept
Definition result.h:412
Result(impl::Err< void > &&arg) noexcept
Definition result.h:394
bool operator!=(const Result &other) const noexcept
Definition result.h:409
Result(Result &&other) noexcept=default
void ValueType
Definition result.h:385
Result(impl::Ok< void > &&arg) noexcept
Definition result.h:389
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:273
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:312
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:429
impl::Err< void > Err() noexcept
If E is void, use the void specialization of Err.
Definition result.h:439
impl::Ok< void > Ok() noexcept
If T is void, use the void specialization of Ok.
Definition result.h:434
Result< void, std::monostate > BoolResult
True or false result.
Definition result.h:284
Definition assert.h:17
STL namespace.