Branch data 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 : 5 : Result() : m_variant{std::in_place_index_t<1>{}, std::monostate{}} {} // constructor for void 47 : 0 : Result(T obj) : m_variant{std::in_place_index_t<1>{}, std::move(obj)} {} 48 : 0 : 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 : 5 : 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 : 0 : T& value() LIFETIMEBOUND 59 : : { 60 [ # # # # : 0 : assert(has_value()); # # # # ] 61 : 0 : 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 : 0 : T value_or(U&& default_value) && 70 : : { 71 [ # # ]: 0 : return has_value() ? std::move(value()) : std::forward<U>(default_value); 72 : : } 73 : 5 : explicit operator bool() const noexcept { return has_value(); } 74 : 0 : const T* operator->() const LIFETIMEBOUND { return &value(); } 75 : 0 : const T& operator*() const LIFETIMEBOUND { return value(); } 76 : 0 : T* operator->() LIFETIMEBOUND { return &value(); } 77 : 0 : T& operator*() LIFETIMEBOUND { return value(); } 78 : : }; 79 : : 80 : : template <typename T> 81 : 0 : bilingual_str ErrorString(const Result<T>& result) 82 : : { 83 [ # # # # : 0 : return result ? bilingual_str{} : std::get<0>(result.m_variant); # # # # ] 84 : : } 85 : : } // namespace util 86 : : 87 : : #endif // BITCOIN_UTIL_RESULT_H