result.h raw
1 // Copyright (c) 2022-present The Bitcoin Core developers
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or https://www.opensource.org/licenses/mit-license.php.
4
5 #ifndef BITCOIN_UTIL_RESULT_H
6 #define BITCOIN_UTIL_RESULT_H
7
8 #include <attributes.h>
9 #include <util/translation.h>
10
11 #include <variant>
12
13 namespace util {
14
15 struct Error {
16 bilingual_str message;
17 };
18
19 //! The util::Result class provides a standard way for functions to return
20 //! either error messages or result values.
21 //!
22 //! It is intended for high-level functions that need to report error strings to
23 //! end users. Lower-level functions that don't need this error-reporting and
24 //! that only need error-handling should avoid util::Result and instead use
25 //! util::Expected, std::optional, std::variant, or custom structs
26 //! and enum types to return function results.
27 //!
28 //! Usage examples can be found in \example ../test/result_tests.cpp, but in
29 //! general code returning `util::Result<T>` values is very similar to code
30 //! returning `std::optional<T>` values. Existing functions returning
31 //! `std::optional<T>` can be updated to return `util::Result<T>` and return
32 //! error strings usually just replacing `return std::nullopt;` with `return
33 //! util::Error{error_string};`.
34 template <class M>
35 class Result
36 {
37 private:
38 using T = std::conditional_t<std::is_same_v<M, void>, std::monostate, M>;
39
40 std::variant<bilingual_str, T> m_variant;
41
42 //! Disallow copy constructor, require Result to be moved for efficiency.
43 Result(const Result&) = delete;
44
45 //! Disallow operator= to avoid confusion in the future when the Result
46 //! class gains support for richer error reporting, and callers should have
47 //! ability to set a new result value without clearing existing error
48 //! messages.
49 Result& operator=(const Result&) = delete;
50 Result& operator=(Result&&) = delete;
51
52 template <typename FT>
53 friend bilingual_str ErrorString(const Result<FT>& result);
54
55 public:
56 Result() : m_variant{std::in_place_index_t<1>{}, std::monostate{}} {} // constructor for void
57 Result(T obj) : m_variant{std::in_place_index_t<1>{}, std::move(obj)} {}
58 Result(Error error) : m_variant{std::in_place_index_t<0>{}, std::move(error.message)} {}
59 Result(Result&&) = default;
60 ~Result() = default;
61
62 //! std::optional methods, so functions returning optional<T> can change to
63 //! return Result<T> with minimal changes to existing code, and vice versa.
64 bool has_value() const noexcept { return m_variant.index() == 1; }
65 const T& value() const LIFETIMEBOUND
66 {
67 assert(has_value());
68 return std::get<1>(m_variant);
69 }
70 T& value() LIFETIMEBOUND
71 {
72 assert(has_value());
73 return std::get<1>(m_variant);
74 }
75 template <class U>
76 T value_or(U&& default_value) const&
77 {
78 return has_value() ? value() : std::forward<U>(default_value);
79 }
80 template <class U>
81 T value_or(U&& default_value) &&
82 {
83 return has_value() ? std::move(value()) : std::forward<U>(default_value);
84 }
85 explicit operator bool() const noexcept { return has_value(); }
86 const T* operator->() const LIFETIMEBOUND { return &value(); }
87 const T& operator*() const LIFETIMEBOUND { return value(); }
88 T* operator->() LIFETIMEBOUND { return &value(); }
89 T& operator*() LIFETIMEBOUND { return value(); }
90 };
91
92 template <typename T>
93 bilingual_str ErrorString(const Result<T>& result)
94 {
95 return result ? bilingual_str{} : std::get<0>(result.m_variant);
96 }
97 } // namespace util
98
99 #endif // BITCOIN_UTIL_RESULT_H
100