Line data Source code
1 : // Copyright (c) 2022 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 : //! only need error-handling should avoid util::Result and instead use standard 25 : //! classes like std::optional, std::variant, and std::tuple, 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 : template <typename FT> 43 : friend bilingual_str ErrorString(const Result<FT>& result); 44 : 45 : public: 46 10010 : Result() : m_variant{std::in_place_index_t<1>{}, std::monostate{}} {} // constructor for void 47 8888 : Result(T obj) : m_variant{std::in_place_index_t<1>{}, std::move(obj)} {} 48 1200 : Result(Error error) : m_variant{std::in_place_index_t<0>{}, std::move(error.message)} {} 49 : 50 : //! std::optional methods, so functions returning optional<T> can change to 51 : //! return Result<T> with minimal changes to existing code, and vice versa. 52 34665 : bool has_value() const noexcept { return m_variant.index() == 1; } 53 0 : const T& value() const LIFETIMEBOUND 54 : { 55 0 : assert(has_value()); 56 0 : return std::get<1>(m_variant); 57 : } 58 8884 : T& value() LIFETIMEBOUND 59 : { 60 8884 : assert(has_value()); 61 8884 : return std::get<1>(m_variant); 62 : } 63 : template <class U> 64 : T value_or(U&& default_value) const& 65 : { 66 : return has_value() ? value() : std::forward<U>(default_value); 67 : } 68 : template <class U> 69 5064 : T value_or(U&& default_value) && 70 : { 71 5064 : return has_value() ? std::move(value()) : std::forward<U>(default_value); 72 : } 73 20709 : explicit operator bool() const noexcept { return has_value(); } 74 : const T* operator->() const LIFETIMEBOUND { return &value(); } 75 : const T& operator*() const LIFETIMEBOUND { return value(); } 76 0 : T* operator->() LIFETIMEBOUND { return &value(); } 77 3820 : T& operator*() LIFETIMEBOUND { return value(); } 78 : }; 79 : 80 : template <typename T> 81 705 : bilingual_str ErrorString(const Result<T>& result) 82 : { 83 705 : return result ? bilingual_str{} : std::get<0>(result.m_variant); 84 : } 85 : } // namespace util 86 : 87 : #endif // BITCOIN_UTIL_RESULT_H